{"id":29970705,"url":"https://github.com/chrisnajman/meal-item-selector","last_synced_at":"2025-08-04T06:02:45.597Z","repository":{"id":305096522,"uuid":"1021901699","full_name":"chrisnajman/meal-item-selector","owner":"chrisnajman","description":"A checkbox-driven meal item manager that enforces selection limits for precise user input control.","archived":false,"fork":false,"pushed_at":"2025-07-18T09:32:58.000Z","size":368,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-18T10:29:04.605Z","etag":null,"topics":["accessibility","aria-attributes","css-flexbox","css-grid","css-nesting","cssnano","es6-modules","esbuild","html-css-javascript","html-minifier-terser","loading-spinner","postcss","theme-switcher"],"latest_commit_sha":null,"homepage":"https://chrisnajman.github.io/meal-item-selector/","language":"HTML","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}},"created_at":"2025-07-18T06:09:06.000Z","updated_at":"2025-07-18T09:33:01.000Z","dependencies_parsed_at":"2025-07-18T10:44:22.104Z","dependency_job_id":null,"html_url":"https://github.com/chrisnajman/meal-item-selector","commit_stats":null,"previous_names":["chrisnajman/meal-item-selector"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/chrisnajman/meal-item-selector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fmeal-item-selector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fmeal-item-selector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fmeal-item-selector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fmeal-item-selector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisnajman","download_url":"https://codeload.github.com/chrisnajman/meal-item-selector/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fmeal-item-selector/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268656528,"owners_count":24285337,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-04T02:00:09.867Z","response_time":79,"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","css-flexbox","css-grid","css-nesting","cssnano","es6-modules","esbuild","html-css-javascript","html-minifier-terser","loading-spinner","postcss","theme-switcher"],"created_at":"2025-08-04T06:00:51.465Z","updated_at":"2025-08-04T06:02:45.528Z","avatar_url":"https://github.com/chrisnajman.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Meal Item Selector\n\nA checkbox-driven meal item manager that enforces selection limits for precise user input control.\n\n[View on GitPage](https://chrisnajman.github.io/meal-item-selector)\n\n---\n\n## How it works\n\nUsers select items from five food categories — Starters, Mains, Vegetables, Desserts, and Drinks — with selection limits enforced per category. Selections are visually displayed with corresponding icons and prices, and a running total is calculated. The interface is fully keyboard-accessible and includes accessibility features such as live region announcements and focus management.\n\n---\n\n## Features\n\n- Checkbox-based selection grouped by food categories\n- Custom limits per group (e.g., max 2 starters, 1 main)\n- Dynamic feedback when limits are exceeded\n- Lazy-loaded images for performance (after first image)\n- Total price calculation on form submission\n- ARIA live region support for screen reader announcements\n- \"Skip to submit\" keyboard links per group\n- Responsive and accessible interface\n- Modular JavaScript and CSS structure\n- Light/dark theme toggle with local storage support\n\n---\n\n## JavaScript\n\nBuilt with **vanilla ES6 JavaScript**, focusing on modern syntax and browser APIs, then bundled, transpiled to ES2015, and minified for broad browser compatibility.\n\nThe JavaScript has been split into separate modules, improving code modularity:\n\n### Main\n\n- `meal-item-selector/`\n  - `meal-item-selector.js`: Initializes the form logic, binds event handlers, and controls flow from input to output on submission.\n  - `components/`\n    - `checkbox-groups.js`: Defines the five checkbox groups and their individual limits using data attributes.\n    - `enforce-checkbox-limits.js`: Dynamically prevents users from exceeding the selection limits for each group, providing inline warnings.\n    - `output-checkbox-values.js`: Builds and appends a DOM structure showing the selected items, their icons, and prices.\n    - `output-prices.js`: Totals all selected checkbox prices and displays the result in the output container.\n    - `remove-no-checkbox-selected-warning.js`: Removes the \"no selection\" warning once a checkbox is selected after an empty submission.\n- `image-credits.js`: Applies lazy-loading to icons used on the image credits page, skipping the first image for eager loading.\n- `helpers/`\n  - `lazy-load-images.js`: Tracks image count to apply `loading=\"lazy\"` to all but the first image.\n  - `set-multiple-attributes.js`: Utility function to assign multiple attributes to a single DOM element efficiently.\n\n### Other\n\n- `loader.js`: Displays a loader animation until the page is fully rendered, then removes the loader and announces readiness for screen readers.\n- `theme.js`: Handles theme toggling (light/dark mode) and local storage management.\n\n---\n\n## CSS\n\nBuilt with modern CSS features such as nesting, custom properties, and the `:has()` pseudo-class, this project emphasizes modular, accessible, and maintainable styling.\n\nThe main `style.css` file serves as an entry point and imports individual CSS modules using `@import`. These are then processed by PostCSS (with `postcss-import`, `postcss-nesting`, and `cssnano`) during the build.\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---\n\n## Accessibility\n\nThe site is fully navigable using tab keys and arrow keys. Key accessibility features include:\n\n- ARIA `role=\"alert\"` used to announce output content changes to screen readers.\n- `tabindex=\"-1\"` and `.focus()` used to direct keyboard focus after form submission.\n- Skip links (`a.skip-link`) placed after each food group to quickly jump to the submit button.\n- Warnings (e.g., for over-selection or missing selections) are announced and clearly styled.\n- Lazy-loading excludes the first image to ensure immediate content visibility for screen reader users on slower connections.\n\n---\n\n## Testing and Compatibility\n\nThe application has been tested on the following platforms and browsers:\n\n- **Operating System**: Windows 10\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---\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---\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\nIn 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","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisnajman%2Fmeal-item-selector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisnajman%2Fmeal-item-selector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisnajman%2Fmeal-item-selector/lists"}