An Angular 16 portfolio & showcase application featuring accessible UI components, theming (light/dark/high-contrast), internationalization scaffolding, sitemap generation, analytics integration hooks, and modular architecture for future expansion (blog, admin, visitor analytics, messaging, Spotify/GitHub integrations, Azure AD B2C auth skeleton).
- Overview
- Key Features
- Tech Stack
- Architecture & Project Structure
- Getting Started
- Available NPM Scripts
- Configuration & Environment
- Theming & Accessibility
- Internationalization (i18n)
- SEO, Sitemap & Robots
- Analytics & Telemetry
- Development Guidelines (Scaffolding Reference)
- Future Roadmap
- Contributing
- License
This repository contains a personal / professional portfolio web application built with Angular 16 and Material Design principles. It emphasizes progressive enhancement, accessibility (WCAG-aligned improvements), content structure, and readiness for future service integrations (authentication, analytics, file handling, visitor tracking, messaging, Spotify, GitHub data, etc.).
- Angular 16 + Standalone-friendly module structure (classic NgModules retained for organization)
- Responsive layout with Bootstrap 5 & custom SCSS theme variables
- Light / Dark theme toggle with persistent preference
- High Contrast mode for accessibility (stored in
localStorage) - Accessible command palette (dialog + combobox ARIA pattern)
- Enhanced navigation semantics (
aria-current, skip link, structured landmarks) - Contact form with validation, live status + assertive error summary region
- SEO assets:
sitemap.xml,robots.txt, dynamic sitemap generator script - i18n foundation (
assets/i18n/en.json,@ngx-translate/*) - Modular service layers (files, messages, page views, visitors, analytics placeholders)
- Environment-based API endpoint mapping (dev vs prod)
- Image & static asset organization
- Future-ready integration endpoints (GitHub, Spotify, Azure AD B2C, AWS-like user APIs)
| Layer | Technology |
|---|---|
| Framework | Angular 16.x |
| UI Toolkit | Angular Material, Bootstrap 5, Font Awesome |
| Styling | SCSS (theme.scss) + CSS custom properties |
| Routing | Angular Router |
| i18n | @ngx-translate/core |
| Auth (planned) | Azure AD B2C (@azure/msal-angular) skeleton config |
| Analytics (optional) | ngx-google-analytics (present) + custom services |
| Build | Angular CLI |
| Deployment (example) | Static hosting (Firebase config present) |
| Tooling | ESLint, Karma/Jasmine tests |
High-level directories:
src/
app/
components/ # Feature + UI components (home, general, etc.)
models/ # Data models & interfaces
services/ # Layered service APIs (analytics, files, messages, etc.)
pipes/ # Custom pipes (e.g., SafeUrl)
animations/ # Reusable animation definitions
assets/
i18n/ # Translation JSON files
images/ # Static images / previews
files/ # PWA manifest and related
environments/ # environment.ts & environment.prod.ts
tools/
generate-sitemap.mjs # Route-based sitemap generator
Key design notes:
- Services isolate endpoint templates (
{orgId},{userName},{pageId}) for dynamic substitution. - Accessibility-first component updates (header, command palette, contact form) focus on keyboard navigation & screen reader clarity.
- Theming handled via CSS variables toggled at the
bodylevel.
Prerequisites:
- Node 18+ (recommended) & npm
- Angular CLI (
npm install -g @angular/cli)
Install dependencies:
npm installRun dev server:
npm start
# Visit http://localhost:4200/Generate sitemap before a production build (automatically chained in build script):
npm run buildServe production build (example using http-server):
npm i -g http-server
http-server dist/omkr.web-app.portfolio| Script | Purpose |
|---|---|
start |
Run dev server (Angular live reload) |
build |
Generate sitemap then build production bundle |
sitemap |
Manually regenerate src/sitemap.xml |
test |
Run unit tests (Karma/Jasmine) |
lint |
Run ESLint over application source |
watch |
Build in watch mode (development) |
Environment files: src/environments/environment.ts & .prod.ts.
Notable dev settings (non-sensitive examples shown):
awsUserApiBaseUrl: 'https://dev-api-v2.keshavsingh.net'
contactApiBaseUrl: 'https://dev-api-v2.keshavsingh.net'
blogUrl / adminUrl
github: { clientId, redirectUri, username }
AzureAdB2C: { tenantName, clientId, policies, logoutRedirectUri }
scopes: { weather: [...], user: [...] }Sensitive values (API keys like x-api-key) should be externalized for production via build-time injection or server-driven proxies. Do NOT commit real secrets to version control.
Placeholder segments like {orgId}, {userName}, {pageId}, {key} are replaced at runtime by services before HTTP calls.
Implemented accessibility features:
- Skip link for keyboard users
- Single
<main>landmark enforcement - Hidden structural
<h1>for consistent document outline - High contrast mode (
body.high-contrastclass; persisted) - Theme toggle with
aria-pressedstate - Command palette: dialog + combobox semantics (
role="dialog",aria-activedescendant, listbox/options) - Live regions: polite status + assertive error summary for forms
- Improved focus styling and respect for
prefers-reduced-motion - Back-to-top button hidden from AT when not visible
High Contrast Mode: stored in localStorage key highContrast ('1' | '0').
See docs/development-guidelines.md for detailed theming + accessibility conventions (focus states, variable usage, high contrast policy, and checklist for new components).
The application now includes a dedicated /resume route providing a print‑optimized résumé view:
- Minimal layout (no navigation, theme toggles, or interactive UI in print)
- Semantic sections: Summary, Experience, Skills, Education, Links
- Screen-only Print button triggers
window.print() - Global
@media printstylesheet hides non-essential elements and adds link URL suffixes
To customize:
- Edit
resume.component.htmlcontent blocks - Adjust print tweaks in
resume.component.cssand globalstyles.css@media printrules - Replace placeholder email / experience entries with real data
Future a11y enhancements (roadmap candidates):
- Automated axe-core audits in CI
- Focus trap utility for dialogs/modals
- Language switcher controlling
<html lang> - More robust error message association using
aria-describedby
Text comes from the API, not from this build. English and Hindi ship, and a language picker appears in the header as soon as more than one language is enabled.
The templates still use | translate — nothing about them changed. What changed is where ngx-translate
gets its data:
ApiTranslateLoader fetches from the identity
provider (environment.idpApiBaseUrl) and merges three layers, later winning:
assets/i18n/<lang>.json— kept purely as an offline base. If the API is unreachable the site still renders real English text rather than raw key names. It is no longer the source of truth.- Structured content —
GET /api/website-content/public/portfolio/{about|experience}?locale=xx. The about paragraphs and the experience timeline are arrays of objects, which a flat translation bundle cannot express, so they live inwebsite_content— which is per-locale by design. - The flat bundle —
GET /api/i18n/bundle/<lang>?ns=portfolio,common,brand. Every scalar string. Untranslated keys are filled in from the language's fallback chain server-side.
Language resolution (stored choice → browser preference → server default), persistence, and polling for
editor changes are handled by the shared @keshavsingh3197/web-config package via
I18nService. Branding, icons, cross-site links and feature
flags come from the same place through
RuntimeConfigService.
Nothing in this repo. On the admin app's Localization screen: add the language, export the default
one as Excel, translate, import it back, enable it. Open tabs pick it up on their next poll. Full model
and endpoints: admin/docs/LOCALIZATION.md.
.npmrc points @keshavsingh3197/* at GitHub Packages and needs PACKAGES_READ_TOKEN (read:packages) in
the environment — the same token the private NuGet feed uses. Before the package's first publish,
tsconfig.json falls back to the sibling checkout's dist/, so run npm run build once in
KeshavSingh-Packages-Web.
tools/generate-sitemap.mjsparsesapp-routing.module.tsand emitssrc/sitemap.xml.- Output includes priority heuristic & last modified date.
robots.txtand duplicatesitemap.xmlalso mirrored undersrc/assets/for hosting flexibility.- Add meta tags / structured data via a future SEO service (
SeoServicealready present for titles/descriptions).
Analytics, PageView, and Visitor services provide an abstraction layer for tracking. Integrations can push events to:
- Google Analytics (via
ngx-google-analytics) - Custom backend endpoints (defined in environment endpoint maps)
Add error monitoring (Sentry/App Insights) by wrapping a provider at AppModule level.
Common Angular generation commands:
ng g component path/to/feature/your-component
ng g service path/to/feature/your-service
ng g guard auth/auth-guard
ng g interface models/thing --type=model
ng g enum models/status
ng g module feature/feature-name --routing
ng g directive shared/directives/your-directive
ng g pipe shared/pipes/your-pipeRun linter:
ng lintAdd 3rd party feature schematics:
ng add <package-name>- Re-introduce automated accessibility tests (axe-core) via Playwright or Jest + jsdom
- Add lazy loading boundaries for feature areas (home subsections / admin)
- Implement PWA enhancements (service worker, offline caching, manifest pruning)
- Dark mode contrast tuning & custom theme editor
- Performance budgets & bundle analysis (e.g.
source-map-explorer) - Image optimization pipeline (WebP/AVIF + responsive sources)
- Integrate GitHub API (recent repos/activity) & Spotify now-playing widget
- Enhanced security: strict CSP headers, SRI hashes for external CDNs
- CI pipeline (GitHub Actions) for lint + test + build + deploy
- Error monitoring integration (Sentry / Azure App Insights)
- Internationalization expansion (hi, es, fr, etc.)
Contributions, issues, and suggestions are welcome.
- Fork the repository
- Create a feature branch:
git checkout -b feat/awesome-thing - Commit changes:
git commit -m "feat: add awesome thing" - Push branch:
git push origin feat/awesome-thing - Open a Pull Request describing motivation & changes
Coding style:
- Follow Angular & ESLint rules (
npm run lint) - Prefer accessible HTML first; only add ARIA when needed
- Keep service method names verb-based and model interfaces noun-based
Extended practices (architecture, a11y checklist, print rules, theming tokens) are documented in docs/development-guidelines.md.
If no LICENSE file is present this project currently defaults to “All rights reserved” by the author. To make it open source under MIT, add a LICENSE file (see suggestion section in repository issues or ask the maintainer).
For quick auditing, notable implemented patterns:
.visually-hiddenutility for screen-reader-only text- Skip link jumps to main content region
- Single
<main>landmark maintained - Header nav uses semantic anchors w/
aria-current - Hidden
<h1>preserves logical heading outline - Command palette: dialog, focus restore, active descendant for list keyboard navigation
- Contact form: autocomplete hints, assertive error summary, polite status region
- High contrast & theme toggles with persisted state
- Back-to-top hidden from AT when off-screen
Do not expose real API keys or secrets in committed environment.ts files for production. Use environment variable replacement or remote configuration.
For questions open an issue or reach out via the contact form implemented in the app.
Enjoy building & iterating! 🔧
