Home

GitHub User Search App

Abstract:
A FrontendMentor.io build that looks up GitHub users via the API, and includes light and dark modes. Mine runs on vanilla JS and Vite.

Built with:
Mobile-first workflow, semantic HTML, Flexbox, CSS Grid, JavaScript, Vite, Octokit API, localStorage, CSS Shadow Palette Generator

view challenge spec arrow_outward

visit live site arrow_outward

Screen grab of GitHub user search app

JavaScript

Octokit

It turned out that the GitHub API has its own library called Octokit. I ended up using octokit/rest.js, because I found a note somewhere suggesting this version would work best with Vite. However, Vite still didn’t like it. I eventually solved the issue by replacing node-fetch with isomorphic-fetch as suggested in this StackOverflow answer (and some others). After that, retrieving public info from GitHub went just fine:

import { Octokit } from "@octokit/rest";

const octokit = new Octokit({
// this is where you would add auth, but it's not necessary to retrieve this public info
// 'userAgent' is still required
    userAgent: 'github-user-search-app Frontendmentor.io exercise',
})

async function fetchResponse(query) {
    try {
    // Octokit includes default headers, don't need to add unless they need to be changed
    const response = await octokit.rest.users.getByUsername({
        username: `${query}`,
    });

    displayResponse(response.data);

    } catch (error) {
        noResults.style.display = 'block';
        console.log(`Error! Status: ${error.status}. Rate limit remaining: ${error.response.headers["x-ratelimit-remaining"]}. Message: ${error.response.data.message}.`)
    }
}

Date formatting

FrontendMentor.io happens to be based in the UK, so the user’s “Joined” date is displayed in the app comps like so: 08 Jul 2020. I wasn’t sure how I was going to get the data returned by GitHub into that format, but I was sure there would be a way to do it with built-in methods instead of a bunch of string manipulations and regexes. After spending some time looking it up, that way turned out to be the Intl.DateTimeFormat() constructor. It has to be made into a standard Date object first, then you give it the parameters you want:

export function displayResponse(response) {
    const date = new Date(response.created_at);
    outputBox.innerHTML = `

    // etc...

        <p class="joined">Joined ${new Intl.DateTimeFormat("en-GB", {
          year: "numeric",
          month: "short",
          day: "2-digit",
        }).format(date)}</p>

    // etc
    `
}

Darkmode

Like my other app builds with both light and dark modes, this checks localStorage in case a user has been to the site before and saved a preference, then checks prefers-color-scheme if not.

const localPref = JSON.parse(localStorage.getItem('darkMode')); // boolean or null
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches; // boolean
let darkMode = localPref ?? defaultDark; // boolean; if this evaluates to true, darkMode should be turned on

// etc...

// app saves a preference to localstorage only if you click the toggle
function handleMode() {
    if (!darkMode) {
        document.body.classList.add('dark-mode');
        localStorage.setItem('darkMode', true);
    } else if (darkMode) {
        document.body.classList.remove('dark-mode');
        localStorage.setItem('darkMode', false);
    }
    darkMode = !darkMode;
}

// this sets darkmode without saving it if it's found in the system preferences only
function setDarkPref(darkMode) {
    if (darkMode) {
        document.body.classList.add('dark-mode');
    } 
}

Resources

Josh Comeau’s amazing CSS Shadow Palette Generator.