An HSL color swatch grid application that discovers distinct named colors using The Color API.
Enter Saturation and Lightness values, and the app displays all distinct named colors for those HSL coordinates. Colors are discovered using an efficient binary search algorithm with parallel fetching and cached in localStorage for instant repeat requests.
- Binary search algorithm
- Parallel HTTP requests with concurrency limiting
- localStorage caching for instant repeat requests
- Responsive grid layout
# Using Docker Compose (recommended)
docker compose up --buildcd frontend
npm install
npm run devOpen http://localhost:3000 (or the port shown in terminal).
The project includes comprehensive tests at three levels:
| Type | Framework | Count |
|---|---|---|
| Unit | Vitest | 43 tests |
| Feature | Vitest + Vue Test Utils | 33 tests |
| E2E | Playwright | 12 tests |
cd frontend
# Run all unit & feature tests
npm test
# Run specific test suites
npm run test:unit # Unit tests only
npm run test:feature # Feature tests only
# Coverage report
npm run test:coverage
# E2E tests (requires dev server)
npm run test:e2eTests run automatically on push via GitHub Actions (.github/workflows/test.yml):
- Unit & feature tests run in parallel with E2E tests
- Playwright report uploaded as artifact on failure
- User adjusts S/L sliders (debounced 300ms)
- Check localStorage for cached swatches
- Cache hit: Load all swatches instantly
- Cache miss: Run binary search with parallel fetching
- Discovery complete: Cache final result in localStorage
- Display swatches sorted by hue
All settings are centralized in src/shared/config.js:
export const config = {
api: {
baseUrl: 'https://www.thecolorapi.com',
},
algorithm: {
startingSamples: 20, // Initial sample points (0 to 359)
concurrencyLimit: 20, // Max parallel HTTP requests
},
defaults: {
saturation: 100, // Initial saturation value
lightness: 50, // Initial lightness value
},
}Breakpoints are configured via Vuetify in src/plugins/vuetify.js.
UI: I paired sliders for quick experimentation with text fields for precise value entry. For loading state, I used placeholder skeleton cards so users see exactly where colors will appear, reinforced by a status badge that transitions from "discovering..." to "found". I made the layout responsive. Controls and grid stack vertically on mobile while expanding horizontally on larger screens.
Performance: I chose binary search to reduce the search space from 360 brute-force API calls, starting with a set # of samples, and searching between them in parallel for new colors. I exposed startingSamples and concurrencyLimit in config to give direct control over the tradeoff between discovery speed and API resource consumption. In practice, I was unable to hit a rate limit during testing. I cache results in localStorage with no expiration, making repeat requests instant.
MIT
