Project Overview:


For this project, I developed a responsive web application designed to display Detroit Lions player performance data based on user-selected position groups.

Objective:


The goal was to create a clean, interactive web app that accepts user input and updates the UI accordingly. The project required separating structured data from HTML, using tools like JavaScript objects, responsive CSS with REM units, and valid semantic HTML. It also had to function smoothly across all screen sizes and include thoughtful design elements like a site icon and accessible navigation.

My Approach:


I started by wireframing the layout and mapping out the data structure. From there, I built a local JavaScript object to hold player stats and used DOM manipulation to dynamically generate content based on the user’s selection from a dropdown menu.


To keep things clean and responsive, I styled the interface using Flexbox and scaled all spacing and typography with REM units. Every UI element — from the dropdown input to the player stat cards — was designed to adapt to smaller screens. I also incorporated accessibility-friendly HTML and ensured performance by minimizing unnecessary assets.


The result is a lightweight, functional web app that showcases player stats clearly and efficiently based on user interaction.

Wireframe Mapping

Before diving into development, I created a wireframe to plan the layout and structure of the interface. This helped me visualize how users would interact with the dropdown menu and where the dynamic stats would be displayed. It also ensured a clean, logical flow on both desktop and mobile, guiding decisions around spacing, hierarchy, and responsiveness before writing any code.

Pseudocode

Before jumping into the full coding process, I created pseudocode to help map out the project’s structure and logic. This allowed me to plan how user input would interact with the data and how the UI would update dynamically, helping guide both the design and development phases.

  • Initialize data

Create an object called playerStats

Store arrays of player info grouped by position (running back, quarterback, wide receiver)

  • Setup page elements

Get reference to the dropdown menu (positionSelect)

Get reference to the display area (statsDisplay)

  • Handle user input

When the user selects a position:

Get the selected position value

Call a function to render stats for that position

  • Render stats

Define a function renderStats(position):

Clear the stats display area

If the position exists in playerStats:

Generate HTML content for each player in the selected group

For each player:

Display name, jersey number, and position-specific stats

Insert the generated content into the stats display container

  • Initial state

Set the display area to be empty on page load

  • Styling and layout

Use CSS to:

- Center content with Flexbox

- Style dropdown and player cards

- Ensure responsive design using REM units


Final Interactive Display

HTML Code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Detroit Lions Player Stats</title>
    <link rel="stylesheet" href="styleSheet.css">
    <link rel="icon" href="http://4.bp.blogspot.com/-ohsAayLXtP0/UelsGhS6HZI/AAAAAAAADhY/CNiE5WqCCY4/s1600/detroit-lions-logo.png" type="image/png">
</head>
<body>
  <img src="http://4.bp.blogspot.com/-ohsAayLXtP0/UelsGhS6HZI/AAAAAAAADhY/CNiE5WqCCY4/s1600/detroit-lions-logo.png" alt="Logo" class="logo">

    <div class="container">
        <h1>Detroit Lions Player Stats</h1>
        <div class="input-group">
            <label for="positionSelect">Select Position Group:</label>
            <select id="positionSelect">
              <option value="" selected disabled>Select a position</option>
              <option value="runningBack">Running Back</option>
              <option value="quarterback">Quarterback</option>
              <option value="wideReceiver">Wide Receiver</option>
          </select>
        </div>
        <div id="statsDisplay" class="stats-container">

        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

The HTML provides the foundational structure for the app. I used semantic elements like <head>, <body>, <select>, and <div> to organize the content clearly and accessibly. The <select> dropdown allows users to choose a position group, while a dedicated <div> (#statsDisplay) acts as the dynamic content area where player stats are rendered based on user input.

I also linked an external stylesheet for clean separation of structure and style, added a favicon for visual branding, and ensured proper mobile scaling with the viewport meta tag. These choices support usability, maintainability, and performance across devices.

JavaScript Code

// Local data storage for player stats
const playerStats = {
    runningBack: [
      { name: "Jahmyr Gibbs", number: 26, yards: 1016, carries: 178, touchdowns: 10 },
      { name: "David Montgomery", number: 5, yards: 771, carries: 180, touchdowns: 12 },
    ],
    quarterback: [
      { name: "Jared Goff", number: 16, yards: 3265, completions: 276, touchdowns: 25 },
      { name: "Hendon Hooker", number: 2, yards: 62, completions: 6, touchdowns: 0 },
    ],
    wideReceiver: [
      { name: "Amon-Ra St. Brown", number: 14, yards: 863, receptions: 81, touchdowns: 9 },
      { name: "Jameson Williams", number: 9, yards: 710, receptions: 39, touchdowns: 4 },
      { name: "Kalif Raymond", number: 11, yards: 204, receptions: 16, touchdowns: 2 },
      { name: "Tim Patrick", number: 14, yards: 349, receptions: 27, touchdowns: 2 },
      { name: "Sam LaPorta (TE)", number: 87, yards: 445, receptions: 36, touchdowns: 5 },
    ],
  };
  
  const positionSelect = document.getElementById("positionSelect");
  const statsDisplay = document.getElementById("statsDisplay");
  
  
  function renderStats(position) {
    let htmlContent = []; // Array to hold all HTML content
  
    if (playerStats[position]) {
      htmlContent.push(renderPositionGroup(position));
    }
  
    // Insert content into the statsDisplay container
    statsDisplay.innerHTML = htmlContent.join('');
  }
  
  // Generate HTML
  function renderPositionGroup(position) {
    if (!playerStats[position]) return '';
  
    let groupContent = ["<div class='position-group'>", "<h2>" + position + "</h2>"];
  
    playerStats[position].forEach(function(player) {
      let positionDetails = [];
  
      if (position === 'runningBack') {
        positionDetails.push("<p>Rushing Yards: " + player.yards + "</p>");
        positionDetails.push("<p>Carries: " + player.carries + "</p>");
        positionDetails.push("<p>Rushing Touchdowns: " + player.touchdowns + "</p>");
  
      } else if (position === 'quarterback') {
        positionDetails.push("<p>Passing Yards: " + player.yards + "</p>");
        positionDetails.push("<p>Completions: " + player.completions + "</p>");
        positionDetails.push("<p>Passing Touchdowns: " + player.touchdowns + "</p>");
  
      } else if (position === 'wideReceiver') {
        positionDetails.push("<p>Receiving Yards: " + player.yards + "</p>");
        positionDetails.push("<p>Receptions: " + player.receptions + "</p>");
        positionDetails.push("<p>Receiving Touchdowns: " + player.touchdowns + "</p>");
      }
  
      groupContent.push(
        "<div class='player-card'>",
        "<p><strong>" + player.name + " (#" + player.number + ")</strong></p>",
        positionDetails.join(''),
        "</div>"
      );
    });
  
    groupContent.push("</div>");
    return groupContent.join('');
  }
  
  // Event listener for dropdown
  positionSelect.addEventListener('change', function(e) {
    const selectedValue = e.target.value;
    renderStats(selectedValue);
  });
  
  // Initial render - no data displayed
  statsDisplay.innerHTML = '';

The JavaScript powers the app’s interactivity and dynamic content. I used a local JavaScript object (playerStats) to store structured player data, organized by position group. When a user selects a position from the dropdown, an event listener triggers the renderStats() function, which updates the UI based on the selected group.

The script uses DOM manipulation to generate and insert HTML content dynamically. Conditional logic ensures the right stats are displayed for each position, and the use of functions like renderPositionGroup() keeps the code modular and organized. This approach demonstrates how user input can control the interface through real-time updates.

CSS Code

body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    font-family: Arial, sans-serif;
  }
  
  .logo {
    display: block;
    margin: 0 auto 1.25rem; 
    width: 6.25rem;
    height: auto;
  }
  
  .container {
    background-color: white;
    padding: 1.25rem;
    border-radius: 0.625rem;
    box-shadow: 0.25rem hsl(0, 0%, 0%); 
    width: 37.5rem;
  }
  
  .center-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
  }
  
  h1 {
    text-align: center;
    color: #333;
  }
  
  .input-group {
    margin-bottom: 1.25rem;
  }
  
  select {
    padding: 0.75rem; 
    width: 100%; 
    border: 0.125rem solid #0076B6; 
    border-radius: 0.5rem;
    background-color: #f0f8ff; 
    font-size: 1.125rem; 
    color: #333; 
    cursor: pointer; 
    box-shadow: 0.25rem hsl(0, 0%, 0%); 
  }
  
  select:hover {
    background-color: #f7faf6;
    border-color: #0076B6; 
  }
  
  select:focus {
    outline: none; 
    border-color: #0076B6; 
    box-shadow: 0.5rem #003957; 
  }
  
  .stats-container {
    margin-top: 1.25rem;
  }
  
  .position-group {
    margin-bottom: 1.25rem;
  }
  
  .position-group h2 {
    color: #0076B6;
    text-transform: uppercase;
    font-size: 1.125rem;
    margin-bottom: 0.625rem;
    border-bottom: 0.0625rem solid #ddd;
    padding-bottom: 0.3125rem;
  }
  
  .player-card {
    margin-bottom: 0.625rem;
    padding: 0.625rem;
    border: 0.0625rem solid #0076B6;
    border-radius: 0.3125rem;
    background-color: #f9f9f9;
  }
  
  .player-card p {
    margin: 0.3125rem 0;
  }

The CSS was designed to create a clean, responsive layout that adapts well to different screen sizes. I used Flexbox to center and align elements both vertically and horizontally, making the app functional on smaller screens. All padding, margins, and font sizes use REM units to ensure scalability and accessibility across devices.

Key styling strategies include:

  • Consistent component styling for dropdowns and player cards to enhance usability and visual hierarchy

  • Box shadows, borders, and colors to separate content and improve readability

  • A responsive container width and clear visual feedback (e.g., :hover, :focus states) for better user interaction

  • A light, readable color palette aligned with the Detroit Lions’ branding

This CSS approach ensures the UI is not only visually appealing but also functional and accessible across different devices.