{"id":34865932,"url":"https://github.com/chrisnajman/journal-2025","last_synced_at":"2026-05-01T22:33:24.699Z","repository":{"id":324553195,"uuid":"1097621974","full_name":"chrisnajman/journal-2025","owner":"chrisnajman","description":"A journal web app that saves entries and images locally in the browser using IndexedDB. Supports adding, editing, deleting, lazy-loaded images, and exporting entries as JSON.  (This is an improved update of an earlier journal project.)","archived":false,"fork":false,"pushed_at":"2025-12-25T11:45:31.000Z","size":226,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-27T06:58:47.043Z","etag":null,"topics":["accessibility","aria-attributes","chatgpt-free","client-side-form-validation","css-grid","css-nesting","cssnano","es6-modules","esbuild","html-minifier-terser","html-template","indexeddb","intersection-observer","lazy-loading","loading-spinner","localstorage","no-js","postcss","quota-management","theme-switcher"],"latest_commit_sha":null,"homepage":"https://chrisnajman.github.io/journal-2025/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrisnajman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-16T14:31:57.000Z","updated_at":"2025-12-25T11:45:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/chrisnajman/journal-2025","commit_stats":null,"previous_names":["chrisnajman/journal-2025"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chrisnajman/journal-2025","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fjournal-2025","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fjournal-2025/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fjournal-2025/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fjournal-2025/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisnajman","download_url":"https://codeload.github.com/chrisnajman/journal-2025/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fjournal-2025/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32515838,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accessibility","aria-attributes","chatgpt-free","client-side-form-validation","css-grid","css-nesting","cssnano","es6-modules","esbuild","html-minifier-terser","html-template","indexeddb","intersection-observer","lazy-loading","loading-spinner","localstorage","no-js","postcss","quota-management","theme-switcher"],"created_at":"2025-12-25T22:24:49.309Z","updated_at":"2026-05-01T22:33:24.692Z","avatar_url":"https://github.com/chrisnajman.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Journal (2025)\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong id=\"menu\"\u003eMenu\u003c/strong\u003e\u003c/summary\u003e\n\n- [Description](#description)\n- [Features](#features)\n- [JavaScript](#javascript)\n- [No JS](#no-js)\n- [IndexedDB](#indexeddb)\n- [Running the App from the Local File System](#running-the-app-from-the-local-file-system)\n- [CSS](#css)\n- [Accessibility](#accessibility)\n- [Theme Toggling](#theme-toggling)\n- [Testing and Compatibility](#testing-and-compatibility)\n- [How to Run](#how-to-run)\n- [Build \u0026 Deployment Setup for `/docs` Folder](#build--deployment-setup-for-docs-folder)\n\n\u003c/details\u003e\n\n## Description\n\nA private journal that runs entirely on your device, either via a local server or a remote host. All your entries are stored locally in your browser, so only you can access them.\n\n[Journal (2025) Git Pages site](https://chrisnajman.github.io/journal-2025/)\n\n\u003e [!NOTE]\n\u003e This is an improved update of an [earlier journal project](https://github.com/chrisnajman/journal-with-local-storage).\n\n[Back to menu](#menu)\n\n---\n\n## Features\n\n- Create, edit, and delete journal entries locally in the browser\n- Add titles, text, and optional images with descriptive alt text\n- Lazy loading for images to improve performance\n- Automatic preservation of line breaks in entry text\n- Export all entries to a JSON backup file\n- Fully accessible forms and interactive elements\n- Light and dark theme toggling with local storage persistence\n- Keyboard navigation and ARIA attributes for improved accessibility\n- IndexedDB storage with graceful handling of storage quota limits\n- Undo or cancel actions for entries and images before publishing\n- Custom form validation error messages\n\n[Back to menu](#menu)\n\n---\n\n## How to Use\n\n### Creating an Entry\n\nClick the **Create Entry** button, then complete the following:\n\n- **Add a title** (required)\n- **Add an image** (optional)\n  - Add a descriptive caption in the image description field (optional)\n  - If you change your mind, click the **Remove image** button\n- **Add text** (required)\n\n#### Publishing an Entry\n\n- Click the **Publish** button to save your entry.  \n  Your entry will appear in the journal list immediately.\n\n#### Cancelling an Entry\n\n- Click the **Cancel** button to discard the current entry.  \n  Any unsaved changes will be lost.\n\n### Editing and Deleting Entries\n\n#### Deleting Entries\n\n\u003e [!CAUTION]\n\u003e Deletion is permanent. Use caution.\n\n##### Delete a Single Entry\n\n- Click the **Delete entry** button on the entry you want to remove.\n- A confirmation dialog will appear.\n  - Click **OK** to confirm deletion\n  - Click **Cancel** to abort deletion\n\n##### Delete All Entries\n\n- Click the **Delete all entries** button at the top of the page.\n- A confirmation dialog will appear.\n  - Click **OK** to delete all entries\n  - Click **Cancel** to abort\n\n##### Deleting the Image from an Entry\n\n- Click **Delete image** within the entry editor.\n- A confirmation dialog will appear.\n  - Click **OK** to remove the image\n  - Click **Cancel** to keep the image\n\n#### Editing Entries\n\n##### Editing the Title\n\n- Click **Edit** on the entry.\n- Update the title field.\n- Click **Save** to apply changes.\n\n##### Editing the Text\n\n- Click **Edit** on the entry.\n- Update the text field as desired.\n- Click **Save** to apply changes.\n\n### Text Formatting\n\n#### Form Text Area\n\n- Pressing **Enter** creates line breaks.\n- Use line breaks to separate paragraphs or sections of your text.\n\n#### Published Entry Text Area\n\n- Line breaks made in the form are preserved when the entry is published.\n- You can add additional line breaks when editing a published entry.\n\n### A Note on Image Sizes\n\n- You should aim to keep image sizes as small as possible to save on storage space.\n- Very large images can be resized and optimised using free online software such as:\n  - **[Photopea](https://www.photopea.com/)**, or\n  - **[TinyPNG](https://tinypng.com/)** (excellent for image compression).\n- Storage capacity in the browser is limited. For example, Chrome on Windows 11 allows roughly 140 MB for IndexedDB storage per origin.\n- This means that if the images you upload are a couple of megabytes or more each, you could quickly approach the storage limit and may be unable to add more entries.\n- For storage limits in other browsers, please consult their help or support sections.\n\n### Where Entries Are Saved\n\n- Entries are stored in **IndexedDB** on your device.\n- To inspect stored data:\n  1. Right-click on the page and select **Inspect**\n  2. Go to **Application \u003e Storage \u003e IndexedDB \u003e Journal \u003e Entries**\n\n#### Data Quota\n\nIf you exceed your browser’s storage quota:\n\n- New entries may fail to save.\n- You will see an alert notifying you that storage is full.\n- Consider deleting older entries, removing large images, or exporting entries to JSON before continuing.\n\n### Exporting Entries\n\n- Click the **Export entries to JSON** button to download a `.json` backup of all your journal entries.\n- This backup can be stored safely for your records.\n\n### Limitations\n\n- All entries are stored **locally on your machine**, so only you can access them.\n- Text formatting is limited to **line breaks**.\n- Bold, italic, or other formatting is not available.\n\n### Data Security\n\nAll entries are saved locally in your browser's storage (IndexedDB), so only you can access them. Even when you use the app via a server, such as GitHub Pages, your data stays safely on your device and is never sent anywhere else.\n\n[Back to menu](#menu)\n\n---\n\n## JavaScript\n\nBuilt with **vanilla ES6 JavaScript**, focusing on modern syntax and browser APIs.\n\nThe JavaScript has been split into separate modules, improving code modularity:\n\n- **index.js**: Initializes the app, sets up global modules (theme, sticky shadow, loader), and loads the journal form and display modules with existing entries from IndexedDB.\n- **js-modules/**\n  - **helpers/**\n    - **sticky-shadow.js**: Adds a shadow to the header or footer when the page is scrolled.\n    - **update-attributes.js**: Utility function to add, update, or remove multiple attributes on DOM elements.\n  - **journal/**\n    - **entry-display-helpers/**\n      - **edit-entry.js**: Handles toggling contentEditable and button text when editing entry headings or text.\n      - **index.js**: Exports all helper functions for entry display for easier imports.\n      - **render-entry.js**: Renders a single journal entry in the list, including images and line breaks.\n      - **sanitise-and-format-text.js**: Cleans pasted text and ensures Enter inserts `\u003cbr\u003e` instead of divs.\n      - **toggle-content-editable.js**: Toggles edit mode for text elements and updates the underlying data model.\n      - **toggle-edit-button.js**: Switches button text between “Edit” and “Save” and updates its styling.\n      - **update-delete-all-button.js**: Enables or disables the “Delete all entries” button based on the number of entries.\n    - **entry-form-helpers/**\n      - **alt-text.js**: Shows or hides the alt text input when adding an image.\n      - **character-counter.js**: Updates the alt text character counter and warns when the limit is reached.\n      - **enable-required-fields.js**: Sets required attributes on the entry form inputs.\n      - **form-reset.js**: Resets form fields and counters after submission or cancellation.\n      - **form-validation.js**: Logic for custom for validation error messages.\n      - **index.js**: Aggregates all entry form helper functions for easy import.\n      - **placeholders.js**: Populates default placeholders and clears them on focus.\n    - **export entries/**\n      - **export-entries-button.js**: Manages the Export button’s state and triggers the JSON export process.\n      - **export-entries-to-json.js**: Converts all entries (including images as base64) to a downloadable JSON file.\n    - **storage/**\n      - **handle-storage-error.js**: Handles errors from IndexedDB, including quota exceeded alerts.\n      - **safe-db.js**: Wraps database operations in a try/catch with error handling.\n      - **storage.js**: Provides functions to add, update, delete, and fetch entries from IndexedDB.\n    - **entry-display.js**: Manages rendering the journal list, adding new entries, and handling click events (edit, delete, image removal).\n    - **entry-form.js**: Manages the entry form UI, form submission, image upload, and integrates with storage and display modules.\n  - **observers/**\n    - **lazy-load-intersection-observer.js**: Uses Intersection Observer to lazy-load images as they come into view.\n    - **page-header-resize-observer.js**: Observes the header size and updates `scroll-padding-top` for proper anchor/skip-link scrolling.\n  - **loader.js**: Handles page load animations and loader display for long-running tasks like exporting entries.\n  - **theme.js**: Manages theme toggling, applies the saved theme from localStorage, and ensures accessibility of the toggle button.\n\n[Back to menu](#menu)\n\n---\n\n## No JS\n\n- If JavaScript is disabled, a warning message is displayed and the entry form and all associated elements are hidden. Additionally, the loader animation and theme-toggler cease to function.\n- The `no-js` class provides a simple fallback mechanism that lets you adjust styles and messages when JavaScript is unavailable.\n\n[Back to menu](#menu)\n\n---\n\n## IndexedDB\n\nFor each project using **IndexedDB**:\n\n- Make sure the database name is unique, e.g.\n\n```javascript\n// Journal project\nconst DB_NAME = \"journal2025\"\n\n// To-Do list project\nconst DB_NAME = \"toDoList\"\n\n// Photo gallery project\nconst DB_NAME = \"photoGallery\"\n```\n\nThis will ensure that multiple projects hosted on **GitHub Pages** have separate data stores.\n\n### Launching the project via **LiveServer** (VSCode)\n\nA further step is required if your server is local. In the project root:\n\n- Create a folder called `.vscode`. Inside this\n- create a file called `settings.json`. Inside this, add the following code, e.g.\n\n```json\n{\n  \"liveServer.settings.port\": 5501\n}\n```\n\n- This will launch the project using port 5501.\n- Use a **new port number** for each new project.\n\n\u003e [!NOTE]\n\u003e The default port for LiveServer is 5500. The safe range for custom ports is 5501–5999.\n\n[Back to menu](#menu)\n\n---\n\n## Running the App from the Local File System\n\nIf you want to run the journal directly from your computer (without any servers or build tools), you only need the folder **journal-2025-local**.\n\nTo launch the app:\n\n**1. Open your file browser**\n\n- Windows: File Explorer\n- macOS: Finder\n- Linux: Files (Nautilus/Konqueror/etc.)\n\n**2. Navigate into the `journal-2025-local` folder**\n\n**3. Double-click `index.html`**\n\nThis opens the journal in your default web browser.  \nNo \"Live Server\", no terminal commands, no Node modules - it's completely standalone.\n\n\u003e [!NOTE]\n\u003e All entries are saved locally using **IndexedDB**, in a database named `journal2025local`.\n\n\u003e [!NOTE] \u003e `style.css` has also been minified.\n\n\u003e [!CAUTION]\n\u003e The full application logic is inside the minified file `journal-2025-local.js`.  \n\u003e Do **not** open this file in VSCode if you have auto-formatting enabled — VSCode will automatically unminify it and make the file much larger.  \n\u003e (There is no need to edit this file.)\n\n[Back to menu](#menu)\n\n---\n\n## CSS\n\nThe CSS is split into modules and imported into `style.css`.\n\n[Back to menu](#menu)\n\n---\n\n## Accessibility\n\n- Fully operable via keyboard (tab navigation and Enter/Space interactions)\n- Proper focus management with temporary visual highlighting for new entries\n- All interactive elements and custom form validation error messages have appropriate ARIA attributes (e.g., `role=\"status\"`, `aria-live=\"polite\"`, `aria-label`)\n- Skip links and anchor targets scroll correctly even with a sticky header\n- Form elements include required attributes and descriptive placeholders\n- Loader messages are announced via ARIA live regions\n- Confirmation dialogs for deletion actions ensure user awareness\n- Image alt text is supported for images to maintain accessibility. (This is optional: if no text is supplied, and empty `alt` attribute is applied to the image.)\n\n[Back to menu](#menu)\n\n---\n\n## Theme Toggling\n\nThe application includes a dark mode and light mode toggle:\n\n- The current theme state is stored in **local storage** and applied automatically on page reload.\n- Accessible buttons with appropriate ARIA attributes are used to improve usability.\n\n\u003e [!IMPORTANT]\n\u003e Remember to change `const LOCAL_STORAGE_PREFIX` in `js-modules/theme.js` to a unique identifier.\n\n[Back to menu](#menu)\n\n---\n\n## Testing and Compatibility\n\nThe application has been tested on the following platforms and browsers:\n\n- **Operating System**: Windows 11\n- **Browsers**:\n  - Google Chrome\n  - Mozilla Firefox\n  - Microsoft Edge\n\n### Device View Testing\n\nThe layout and functionality have been verified in both browser and device simulation views to ensure responsiveness and usability.\n\n[Back to menu](#menu)\n\n---\n\n## How to Run\n\n1. Clone or download the repository to your local machine.\n2. Open the project folder and start a simple HTTP server (e.g., using `Live Server` in VS Code or Python's `http.server` module).\n3. Open the project in a modern browser (e.g., Chrome, Firefox, or Edge).\n\n[Back to menu](#menu)\n\n---\n\n## Build \u0026 Deployment Setup for `/docs` Folder\n\nIf you want to deploy a minified version of this project to **GitHub Pages**, read on.\n\n### 1. Install Required Packages\n\nRun this once in your project root to install dev dependencies:\n\n```bash\nnpm install\n```\n\n### 2. Run the full build process\n\n\u003e [!IMPORTANT]\n\u003e Any assets not described in `package.json` must be added. In the current project we don't have an `img` folder. If you create one and add images to it, you have to add this to `copy:assets`, e.g.\n\n#### Current `package.json`\n\n```\n\"copy:assets\": \"shx cp -r  site.webmanifest favicon.ico favicon-16x16.png favicon-32x32.png apple-touch-icon.png android-chrome-192x192.png android-chrome-512x512.png docs/\",\n```\n\n#### Updated `package.json` with \"img\"\n\n```\n\"copy:assets\": \"shx cp -r  img site.webmanifest favicon.ico favicon-16x16.png favicon-32x32.png apple-touch-icon.png android-chrome-192x192.png android-chrome-512x512.png docs/\",\n```\n\netc, etc.\n\nThen in the terminal, run:\n\n```bash\nnpm run build\n```\n\n### 3. Deploy to GitHub Pages\n\nOnce you've created a repository and pushed the files,\n\n- go to `https://github.com/[your-name]/[your-project-name]/settings/pages`.\n- Under \"Build and deployment \u003e Branch\" make sure you set the branch to `main` and folder to `/docs`.\n- Click \"Save\".\n\n\u003e [!NOTE]\n\u003e For a detailed description of the build process, configuration files and npm packages see my [GitHub Pages Optimised Build](https://github.com/chrisnajman/github-pages-optimised-build).\n\n[Back to menu](#menu)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisnajman%2Fjournal-2025","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisnajman%2Fjournal-2025","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisnajman%2Fjournal-2025/lists"}