The grid layout dynamically adapts the number of columns based on the container’s width:
- < 860px → 1 column
- < 1024px → 2 columns
- < 1720px → 3 columns
- ≥ 1720px → 4 columns
This responsive behavior ensures optimal use of screen space across devices, from mobile to widescreen desktops.
Images are loaded lazily as they enter the viewport, selecting the most suitable resolution based on the visible width and screen density (e.g., Retina displays). Once an image is loaded, the system disconnects observers to boost performance. Resize handling is debounced to avoid unnecessary reflows during dynamic layout changes.
- Intersection Observer: Uses the Intersection Observer API to detect when an image enters the viewport.
- Responsive image size: Selects and loads the most appropriate image from a list of sources based on the image’s visible width.
- Retina support: Automatically chooses a higher-resolution image (e.g. 2x) on high-DPR screens for better visual quality.
- Lazy loading: Improves performance and saves bandwidth by loading images only when needed, and removes the observer after the image has loaded to reduce overhead.
- Resize Observer: Uses the Resize Observer API to track element size changes in order to calculate column count.
- Debounced updates: Invokes the callback with a configurable debounce delay (default: 50ms) to reduce update frequency.
- Automatic cleanup: Cancels pending callbacks and disconnects the observer on unmount for optimal performance.
Displays a single photo in an optimized, responsive layout, including metadata and download functionality.
Thanks! Here's a clean and professional technical description for this Photo Details View, written in the same style as earlier:
-
Responsive rendering: Uses the
<picture>element with media queries to serve the correct orientation (portraitorlandscape) based on the device’s current orientation. -
Aspect ratio handling:
ImageMaxAspectRatioensures the image stays within viewport limits while preserving its original proportions. It dynamically calculates max dimensions using metadata extracted from the image URL and intrinsic dimensions. -
Lazy loading & performance: The image loads lazily with
loading="lazy"anddecoding="async", optimizing performance and visual stability. -
Download support: A "Download" button is shown if the original image URL is available. Clicking it triggers a client-side download via a custom hook, with loading state feedback.
-
Photo metadata: Displays the photo’s caption (
alt) and photographer name in a semantically correct layout using<figure>,<figcaption>, and<address>.
The useImageSources hook generates detailed <source> descriptors for the <picture> element to support responsive image loading based on different sizes and orientations. It calculates the aspect ratio and dynamically chooses which image versions to load depending on whether the photo is in landscape or portrait orientation.
but since this page load one image and image src set dosnt provide varios range of size for portrate and landscape i chose to go for only landscape and portrate provideded
<source srcset="src.small" media="(orientation: landscape) and (max-width: 200px)" />
<source srcset="src.medium" media="(orientation: landscape) and (max-width: 500px)" />
<source srcset="src.large, src.large2x" media="(orientation: landscape)" />
<source srcset="src.portrait" media="(orientation: portrait)" /><source srcset="src.small" media="(orientation: portrait) and (max-height: 200px)" />
<source srcset="src.medium" media="(orientation: portrait) and (max-height: 500px)" />
<source srcset="src.large, src.large2x" media="(orientation: portrait)" />
<source srcset="src.landscape" media="(orientation: landscape)" />This approach optimizes image delivery by adapting to screen orientation and size, ensuring the best quality with minimal bandwidth.
Note: However, since this page loads only one image and the srcset does not provide multiple size variations for both portrait and landscape, the implementation focuses only on the main portrait and landscape sources provided.
Here's a concise and clear way you can phrase that in your README to explain your testing coverage and focus:
Due to the limited time available, I have written example tests primarily for UI components to demonstrate testing practices.
Most of the core application logic—especially hooks and utility functions—already have high coverage from previous work on a related CodeChallenge project. Extending full coverage here would require significantly more time, so this codebase currently focuses on component-level testing to showcase integration points.
If you want, I can help you polish or expand it to fit your style or add details about your testing strategy or tools used (Vitest, Testing Library, etc.). Would you like that?
To get started with this project, simply run:
pnpm install
pnpm devHere’s a clear and concise section you can add to your README under Getting Started:
-
Environment Configuration
Copy the default environment file to create your local
.envfile:cp default.env .env
-
API Key Setup
-
If you don't provide an API key in your
.envfile, the application will automatically fall back to using the key defined in the environment page (if available). -
To set it manually, open
.envand set:API_KEY=your_api_key_here
In the project directory, you can run:
Runs the app in development mode using Vite. The page will reload if you make edits. You will also see any lint errors in the console.
Builds the app for production using TypeScript and Vite.
The output is optimized and placed in the dist folder.
Previews the production build locally. Useful for testing before deploying.
Runs ESLint on the entire codebase (.ts and .tsx files).
Fails on unused disable directives and warnings.
Formats the entire codebase using Prettier.
Runs all tests using Vitest in watch mode.
Opens the Vitest UI in your browser for an interactive test view.
Runs tests and generates a coverage report.
Sure! Here's a Production Build section you can add to your README to explain how to build and run your React app with Docker and NGINX:
You can build and run the production version of the app using Docker.
docker build -t your-app-name .docker run -p 8080:80 your-app-nameThis will start the app on http://localhost:8080.
Alternatively, if you have a docker-compose.yml configured, run:
docker compose --env-file prod.env up --buildThen open http://localhost:8080 in your browser.
- The app is served with NGINX configured to support SPA routing.
Got it! Here's a polished Tools Used section with your Vite description plus Vitest and brief reasons why you chose them:
Best Generation Frontend Tooling
- 💡 Instant Server Start for fast development feedback
- ⚡️ Lightning Fast Hot Module Replacement (HMR) to update modules without full reloads
- 🛠️ Rich Features including TypeScript support, CSS preprocessors, and more out of the box
- 📦 Optimized Production Build with Rollup under the hood
- 🔩 Universal Plugin Interface allowing flexible extensions
- 🔑 Fully Typed APIs for excellent TypeScript support and DX
Vite was chosen for its modern architecture that significantly improves development speed and build performance compared to traditional bundlers.
Vitest is a blazing-fast unit testing framework built on top of Vite.
- ⚡️ Super fast test startup due to native ESM and Vite’s dev server
- 🧩 Supports Jest-like APIs, making migration and usage familiar
- 🔍 Integrated with Vite’s ecosystem for smooth testing of modern frontend code
- 📊 Built-in coverage reports and snapshot testing support
Vitest enables rapid, reliable tests with minimal configuration, perfectly complementing the Vite build system.
MSW intercepts actual network requests in both browser and Node environments and returns mocked responses based on your defined handlers.
- 🚦 Enables reliable and fast frontend development and testing without depending on backend availability
- 🔄 Supports seamless integration with your test setup (like Vitest) for end-to-end and integration tests
- 🛠️ Works with real HTTP requests, making mocks more realistic and maintainable
- 🌐 Allows switching between mocked and real APIs effortlessly
MSW improves development speed and test reliability by simulating backend APIs accurately, making your frontend resilient and easier to debug.
- Automates running scripts (like linting and formatting) before commits
- Prevents bad code from being committed by enforcing standards early
- Improves team workflow and codebase consistency
- Prettier: Automatically formats your code to a consistent style, reducing debates over style and saving time
- ESLint: Statically analyzes your code to catch errors, enforce coding conventions, and improve code quality
- Together, they ensure clean, readable, and error-free code, boosting maintainability and developer productivity
- Adds static typing to JavaScript, enabling early detection of errors during development
- Improves code readability and maintainability with clear types and interfaces
- Provides excellent IDE support with autocompletion and refactoring tools
- Helps scale codebases safely, especially in larger projects or teams
TypeScript is chosen to deliver robust, scalable, and maintainable frontend code, reducing runtime errors and enhancing developer experience.
- Enables writing CSS directly inside JavaScript/TypeScript using tagged template literals
- Supports dynamic styling based on props, making components highly reusable and customizable
- Scoped styles prevent CSS conflicts and improve maintainability
- Provides a seamless developer experience with style co-location
- A utility-first style framework that helps build responsive, theme-based design systems quickly
- Provides powerful style props for spacing, layout, color, typography, and more
- Encourages consistency and scalability in styling by leveraging a design token/theme approach
- Integrates smoothly with Styled Components for flexible and maintainable UI styling
Together, Styled Components and Styled System enable a modern, scalable, and maintainable styling approach with powerful theming and responsive design capabilities.