Skip to content

felipeog-org/goodies-grid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Goodies Grid

A responsive product grid built with vanilla HTML, CSS, and JavaScript. No frameworks, no build tools, no external dependencies beyond a Google Font.

Project Overview

Goodies Grid is a front-end-only product catalog that demonstrates responsive layout, DOM manipulation, and interactive UI patterns using web standards exclusively.

Features

  • Responsive grid - 1 column on mobile, 2 on tablets, 3 on desktop
  • Add to Cart toggle - visual state change with cart counter in the header
  • Real-time search - debounced, case-insensitive filtering by product title
  • Dynamic product creation - new cards injected via DOM APIs with full feature parity
  • Image loading states - shimmer animation with fade-in on load
  • Dark mode - automatic via prefers-color-scheme
  • Reduced motion - respects prefers-reduced-motion to disable all animations and transitions
  • Empty state - displayed when no products match the search query

File Structure

index.html      - Semantic markup and page structure
styles.css      - Mobile-first styles, theming, animations
script.js       - DOM interactions, search, dynamic card creation
favicon.svg     - SVG favicon
img/            - Product images

Architecture

HTML

Semantic elements are used throughout: <main>, <header>, <section>, and <article> for product cards. The script is loaded at the end of <body> so the DOM is parsed and ready when JavaScript executes, wrapped in an IIFE to avoid global scope pollution.

CSS

Mobile-first approach. Base styles target narrow viewports. Two min-width media queries progressively enhance the layout:

Breakpoint Grid columns
< 768px 1
>= 768px 2
>= 1024px 3

CSS Grid handles the product layout. Cards use Flexbox internally so the body content stretches and buttons align at the bottom regardless of description length.

Theming is managed entirely through CSS custom properties on :root, overridden inside a prefers-color-scheme: dark media query. No JavaScript is involved in theme switching.

BEM-inspired naming (product-card, product-card__title, product-card--added) keeps selectors flat and predictable.

JavaScript

All code runs inside an IIFE with "use strict". No globals are created.

Event delegation on the product grid container handles Add to Cart clicks for both static and dynamically created cards, avoiding per-button listeners.

Search uses a debounced input listener (300ms) to avoid excessive DOM traversal. A generation counter invalidates stale hide-transition callbacks when the user types faster than transitions complete.

Card hide/show uses CSS transitions (opacity, transform) with a transitionend listener and a fallback setTimeout to guarantee completion even when prefers-reduced-motion suppresses transitions. A done flag prevents duplicate execution from multiple transitionend events.

Dynamic cards are built exclusively with document.createElement() - no innerHTML. New cards are run through filterProducts() immediately so they respect the active search query.

Accessibility

  • Semantic HTML - <article>, <section>, <header>, <main> provide document structure for assistive technology
  • Skip link - "Skip to products" link appears on keyboard focus, allowing users to bypass controls and jump to the grid
  • Visible <label> for the search input (visually hidden but accessible to screen readers), replacing aria-label
  • aria-label on the product grid section
  • aria-pressed on Add to Cart buttons communicates toggle state to screen readers
  • aria-live="polite" on the cart count, result count, and empty state so changes are announced without interrupting the user
  • focus-visible outlines on all buttons for keyboard navigation without affecting mouse users
  • type="search" on the search input for semantic correctness and mobile keyboard optimization
  • Font loaded with display=swap to avoid invisible text during font loading

Performance

  • Debounced search - filtering runs only after 300ms of inactivity, reducing DOM operations during rapid typing
  • Event delegation - a single click listener on the grid handles all cart buttons instead of one per card
  • Shimmer animation cleanup - the shimmer animation is stopped (style.animation = "none") once images load so the browser doesn't run invisible keyframe calculations
  • Image loading - the --loading class is set in HTML (not added by JS) to prevent flash-of-loaded-image on cached assets; img.complete is checked to handle already-cached images synchronously
  • Native lazy loading - loading="lazy" on below-the-fold images defers network requests until needed
  • preconnect hints for Google Fonts reduce connection latency
  • Open Graph meta tags - og:title, og:description, og:type, and og:image for social sharing
  • SVG favicon - resolution-independent, tiny file size, no build step required
  • Forced scrollbar (overflow-y: scroll) prevents layout shift from scrollbar appearance/disappearance during search filtering
  • No frameworks, no build tools - zero overhead; works by opening index.html in a browser

Assumptions

  • Product data and images are placeholders; no backend or persistent storage
  • Cart state is in-memory only and resets on page reload
  • The font (Jost) is loaded from Google Fonts - the only external resource; system fonts are used as fallback
  • Images are served locally from /img/ and assumed to be available