Skip to content

Commit

Permalink
feat: ensure responsiveness on smaller screens. refactor: use css mod…
Browse files Browse the repository at this point in the history
…ules.

on narrow screens, everything is resized. at a certain breakpoint (super narrow mobile screens), we use two columns of 5 rows to better use the limited page space. even narrower portrait will remove the name of the pokemons entirely. css modules improves maintainability by preventing name clashing when scaling application, and also improves performance (better than using css-in-js alternative, or css libraries). fixes #11. note: there is some 'jumping' in the positioning of the header during loading states. i've hacked together a bunch of media queries to fix this as i don't know how to ensure the size of the main section, or the loadingScreen component outer element with css. i don't want to be generating placeholder Card components unnecessarily as this is not performant, but the <main> section essentially needs to be stretched out
  • Loading branch information
henrylin03 committed Nov 27, 2024
1 parent 7dba2e9 commit f049d10
Show file tree
Hide file tree
Showing 20 changed files with 429 additions and 182 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Challenge yourself to try and set a new high score!

[![Screenshot](./docs/screenshot.png)](https://poke-mems.netlify.app/)

This project is part of [The Odin Project's](https://www.theodinproject.com/) "Full Stack JavaScript" course. Built in ReactJS, this project focuses on practising React's `useEffect` hook for handling side effects and API calls. All Pokémon IDs, names, and images are fetched from the [PokéAPI](https://pokeapi.co/).
This project is part of [The Odin Project's](https://www.theodinproject.com/) "Full Stack JavaScript" course. Built in ReactJS, this project focuses on practising React's `useEffect` hook for handling side effects and API calls. All Pokémon IDs, names, and images are fetched from the RESTful [PokéAPI](https://pokeapi.co/).

### Built with

Expand Down
Binary file modified docs/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 10 additions & 19 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useState } from "react";
import Header from "./components/Header";
import Gameboard from "./components/Gameboard";
import Scoreboard from "./components/Scoreboard";
import "./styles/global.css";
import "./styles/header.css";
import styles from "./styles/app.module.css"

function App() {
const [currentScore, setCurrentScore] = useState(0);
Expand All @@ -17,22 +16,14 @@ function App() {
const resetScore = () => setCurrentScore(0);

return (
<>
<header>
<div className="left">
<h1>Pokémems</h1>
<p className="explanation">
Earn points by clicking on a Pokemon... but don&apos;t click on any
of them more than once!
</p>
</div>
<Scoreboard currentScore={currentScore} highScore={highScore} />
</header>

<main>
<Gameboard incrementScore={incrementScore} resetScore={resetScore} />
</main>
</>
<div className={styles.appBody}>
<div className={styles.inner}>
<Header currentScore={currentScore} highScore={highScore} />
<main className={styles.mainSection}>
<Gameboard incrementScore={incrementScore} resetScore={resetScore} />
</main>
</div>
</div>
);
}

Expand Down
15 changes: 10 additions & 5 deletions src/components/Card.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import PropTypes from "prop-types";
import "../styles/card.css";
import styles from "../styles/card.module.css";

const Card = ({ handleCardSelection, pokemonData, isLoading }) => {
return (
<button
className="card"
className={styles.card}
disabled={isLoading}
onMouseDown={() => handleCardSelection(pokemonData.id)}
aria-label={`Select Pokemon, ${pokemonData.name}`}
>
<figure>
<img src={pokemonData.imageUrl} alt={`image of ${pokemonData.name}`} />
<figure className={styles.imageContainer}>
<img
src={pokemonData.imageUrl}
alt={`image of ${pokemonData.name}`}
className={styles.image}
/>
</figure>
<p>{pokemonData.name}</p>
<p className={styles.pokemonName}>{pokemonData.name}</p>
</button>
);
};
Expand Down
11 changes: 3 additions & 8 deletions src/components/Gameboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
shuffleElements,
randomlySelectElements,
} from "../helpers";
import "../styles/gameboard.css";
import styles from "../styles/gameboard.module.css";

const TOTAL_IDS = 10;

Expand All @@ -21,12 +21,7 @@ const Gameboard = ({ incrementScore, resetScore }) => {
const { pokemons, error, isLoading } = useAllPokemon(displayedPokemonIds);

//TODO: handle error
if (isLoading)
return (
<section className="gameboard">
<LoadingScreen />
</section>
);
if (isLoading) return <LoadingScreen />;

const regeneratePokemonIds = (selectedPokemonId) => {
const MAX_IDS_OF_POKEMONS_ALREADY_SELECTED = 5;
Expand Down Expand Up @@ -83,7 +78,7 @@ const Gameboard = ({ incrementScore, resetScore }) => {
};

return (
<section className="gameboard">
<section className={styles.gameboard}>
{pokemons.map((pokemon) => (
<Card
key={pokemon.id}
Expand Down
27 changes: 27 additions & 0 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import PropTypes from "prop-types";
import styles from "../styles/header.module.css";

const Header = ({ currentScore, highScore }) => (
<header className={styles.header}>
<div className={styles.left}>
<h1 className={styles.heading}>Pokémems</h1>
<p className={styles.explanation}>
Earn points by clicking on a Pokemon... but not more than once!
</p>
</div>

<article className={styles.scoreboard}>
<p className={styles.label}>score:</p>
<p className={styles.score}>{currentScore}</p>
<p className={styles.label}>high score:</p>
<p className={styles.score}>{highScore}</p>
</article>
</header>
);

Header.propTypes = {
currentScore: PropTypes.number.isRequired,
highScore: PropTypes.number.isRequired,
};

export default Header;
14 changes: 9 additions & 5 deletions src/components/LoadingScreen.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import masterBall from "../assets/masterball.png";
import "../styles/loadingScreen.css";
import styles from "../styles/loadingScreen.module.css";

export default function LoadingScreen() {
return (
<div className="loadingScreen">
<img className="masterball" src={masterBall} alt="spinning master ball" />
<p>Catching them all...</p>
</div>
<article className={styles.loadingScreen}>
<img
className={styles.masterball}
src={masterBall}
alt="spinning master ball"
/>
<p className={styles.text}>Catching them all...</p>
</article>
);
}
20 changes: 0 additions & 20 deletions src/components/Scoreboard.jsx

This file was deleted.

1 change: 1 addition & 0 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import "./styles/global.css";

createRoot(document.getElementById('root')).render(
<StrictMode>
Expand Down
45 changes: 45 additions & 0 deletions src/styles/app.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.appBody {
background-color: var(--primary-colour);
height: 100dvh;
}

.inner {
max-width: 92rem;
height: 95%;
padding: 48px;
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
gap: 4%;
}

/* MEDIA QUERIES */
@media (max-width: 999px) {
.inner {
max-width: 95dvw;
padding: 4px;
}
}

@media (max-width: 599px) {
.inner {
max-width: 100%;
padding: 20px 12px;
gap: 2%;
}
}

@media (max-width: 399px) {
.inner {
padding: 24px 8px;
}
}

/* small mobile screens, landscape */
@media (max-height: 419px) {
.inner {
max-width: 100%;
padding: 16px 8px 0 8px;
}
}
37 changes: 0 additions & 37 deletions src/styles/card.css

This file was deleted.

103 changes: 103 additions & 0 deletions src/styles/card.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
.card {
min-width: 100%;
height: auto;
background-color: var(--primary-colour-lighter);
border-radius: 16px;
padding: 24px 12px;
box-shadow: 3px 2px 5px 0px rgba(0, 0, 0, 0.25);

/* flexbox */
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}

.pokemonName {
color: white;
text-transform: uppercase;
font-size: .8rem;
letter-spacing: .1rem;
}

.imageContainer {
width: 90%;
height: auto;
background-color: white;
display: grid;
place-items: center;
}

.image {
width: 100%;
}

.card:not([disabled]):hover {
box-shadow: 0 0 44px 8px var(--secondary-colour);
transform: scale(1.1);
}

.card:active {
transform: scale(1);
}

/* MEDIA QUERIES */
@media (max-width: 999px) {
.card {
padding: 16px 4px;
gap: 16px;
}

.pokemonName {
font-size: .5rem;
}
}

@media (max-width: 599px) {
.card {
max-height: 20dvh;
padding: 8px;
gap: 12px;
}

.imageContainer {
width: 100%;
}

.image {
width: auto;
}
}

@media (max-width: 389px) {
.imageContainer {
width: 100%;
border-radius: 12px;
}

.image {
width: auto;
}

.pokemonName {
display: none;
}
}

/* small mobile screens, landscape */
@media (max-height: 419px) {
.card {
max-height: fit-content;
padding: 8px;
gap: 8px;
}

.imageContainer {
width: 100%;
border-radius: 12px;
}

.image {
width: auto;
}
}
7 changes: 0 additions & 7 deletions src/styles/gameboard.css

This file was deleted.

Loading

0 comments on commit f049d10

Please sign in to comment.