├── .eslintrc.json
├── public
├── favicon.ico
├── screenshots
│ ├── Web-Screenshot.png
│ └── Mobile-Screenshot.png
├── assets
│ ├── chevron-back-outline.svg
│ ├── chevron-down-outline.svg
│ ├── chevron-forward-outline.svg
│ └── types
│ │ ├── normal.svg
│ │ ├── grass.svg
│ │ ├── electric.svg
│ │ ├── water.svg
│ │ ├── dark.svg
│ │ ├── ground.svg
│ │ ├── steel.svg
│ │ ├── poison.svg
│ │ ├── fighting.svg
│ │ ├── flying.svg
│ │ ├── ghost.svg
│ │ ├── ice.svg
│ │ ├── bug.svg
│ │ ├── rock.svg
│ │ ├── fairy.svg
│ │ ├── psychic.svg
│ │ ├── fire.svg
│ │ └── dragon.svg
└── vercel.svg
├── next.config.js
├── pages
├── _app.js
├── _middleware.js
└── dex
│ └── [number].jsx
├── components
├── Type
│ └── index.jsx
├── MatchupCard
│ └── index.jsx
├── Navbar
│ └── index.jsx
├── EvolutionCard
│ └── index.jsx
└── StatsCard
│ └── index.jsx
├── .gitignore
├── package.json
├── styles
├── globals.css
├── Navbar.module.css
├── Matchup.module.css
├── Stats.module.css
├── Pokemon.module.css
├── Evolution.module.css
└── Type.module.css
├── README.md
└── lib
├── matchups.js
├── pokemon.js
└── types.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vynex/ToxaDex/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/screenshots/Web-Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vynex/ToxaDex/HEAD/public/screenshots/Web-Screenshot.png
--------------------------------------------------------------------------------
/public/screenshots/Mobile-Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vynex/ToxaDex/HEAD/public/screenshots/Mobile-Screenshot.png
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | reactStrictMode: true,
3 | images: {
4 | domains: ['raw.githubusercontent.com'],
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css'
2 |
3 | function MyApp({ Component, pageProps }) {
4 | return
5 | }
6 |
7 | export default MyApp
8 |
--------------------------------------------------------------------------------
/pages/_middleware.js:
--------------------------------------------------------------------------------
1 | import { NextResponse, NextRequest } from 'next/server';
2 |
3 | export const middleware = async (req, ev) => {
4 | const { pathname } = req.nextUrl;
5 |
6 | if (pathname == '/') return NextResponse.redirect('/dex/1');
7 | return NextResponse.next();
8 | };
9 |
--------------------------------------------------------------------------------
/public/assets/chevron-back-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/chevron-down-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/chevron-forward-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/Type/index.jsx:
--------------------------------------------------------------------------------
1 | import styles from '../../styles/Type.module.css';
2 |
3 | const Type = ({ type }) => {
4 | return (
5 |
9 | );
10 | };
11 |
12 | export default Type;
13 |
--------------------------------------------------------------------------------
/public/assets/types/normal.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "toxadex",
3 | "private": true,
4 | "scripts": {
5 | "dev": "next dev",
6 | "build": "next build",
7 | "start": "next start",
8 | "lint": "next lint"
9 | },
10 | "dependencies": {
11 | "axios": "^0.24.0",
12 | "color.js": "^1.2.0",
13 | "next": "12.0.7",
14 | "react": "17.0.2",
15 | "react-dom": "17.0.2",
16 | "react-icons": "^4.3.1",
17 | "react-swipeable": "^6.2.0",
18 | "tinycolor2": "^1.4.2"
19 | },
20 | "devDependencies": {
21 | "eslint": "8.6.0",
22 | "eslint-config-next": "12.0.7"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/public/assets/types/grass.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/types/electric.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/water.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@500&display=swap');
2 | @import url('https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;500;700;800&display=swap');
3 |
4 | html,
5 | body {
6 | height: 100%;
7 | padding: 0;
8 | margin: 0;
9 | font-family: 'Nunito Sans', sans-serif;
10 |
11 | transition: background-color 200ms ease-in-out;
12 | }
13 |
14 | a {
15 | color: inherit;
16 | text-decoration: none;
17 | }
18 |
19 | * {
20 | box-sizing: border-box;
21 | }
22 |
23 |
24 | @media only screen and (max-width: 1500px) {
25 | body {
26 | font-size: 14px;
27 | }
28 | }
--------------------------------------------------------------------------------
/public/assets/types/ground.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/steel.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/styles/Navbar.module.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | width: 100%;
3 | padding: 0.6rem 0;
4 |
5 | position: absolute;
6 | top: 0;
7 |
8 | display: flex;
9 | justify-content: center;
10 | }
11 |
12 | .links {
13 | display: flex;
14 | align-items: center;
15 | }
16 |
17 | .links>* {
18 | color: #fff;
19 | text-shadow: 1px 1px 0px rgba(0, 0, 0, 0.2);
20 | transition: transform 150ms ease-in-out;
21 | }
22 |
23 | .links>*:hover,
24 | .link.active {
25 | transform: scale(1.1);
26 | }
27 |
28 | .arrow {
29 | margin: 0 0.8rem;
30 |
31 | font-size: 1.4rem;
32 | cursor: pointer;
33 | }
34 |
35 | .link {
36 | margin: 0 0.6rem;
37 | width: 1.2rem;
38 |
39 | font-style: Nunito;
40 | font-weight: 500;
41 |
42 | user-select: none;
43 | }
44 |
45 | @media only screen and (max-width: 1000px) {
46 | .navbar {
47 | display: none;
48 | }
49 | }
--------------------------------------------------------------------------------
/public/assets/types/poison.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/fighting.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/flying.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/ghost.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/ice.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ToxaDex
2 |
3 | A PokéDex App built using Next.js and React. Uses the [PokéAPI](https://pokeapi.co/) directly to fetch data on all 898 Pokémon. Uses Static Site Generation for rendering all the pages.
4 |
5 | [Live on Netlify](https://toxadex.netlify.app)
6 |
7 | ---
8 |
9 | ## 📃 About
10 |
11 | Provides Information on all 898 species of Pokémon including their Genus, Height, Weight, Abilities, Evolution Line, and Stats. Effectiveness against each type is also calculated for every Pokémon in the Type Matchups secion. You can navigate using either the Links in Web Version or by Swiping Left or Right on Touch Devices.
12 |
13 | ---
14 |
15 | ## 📷 Screenshots
16 |
17 | 
18 |
19 | 
20 |
21 | ---
22 |
23 | ## 💡 Running Locally
24 |
25 | ```bash
26 | $ git clone https://github.com/Vynex/toxadex
27 |
28 | $ cd toxadex
29 | $ npm install
30 |
31 | $ npm run dev
32 | ```
33 |
34 | ---
35 |
--------------------------------------------------------------------------------
/public/assets/types/bug.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/types/rock.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/types/fairy.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/psychic.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/assets/types/fire.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/lib/matchups.js:
--------------------------------------------------------------------------------
1 | import typeData from './types.json';
2 |
3 | const getMatchups = (types) => {
4 | const typeNames = types.map((type) => type.type.name);
5 |
6 | const multipliers = {};
7 |
8 | typeNames.forEach((type) => {
9 | const damageRelations = typeData[type];
10 |
11 | let immuneFrom = damageRelations.defense.zero;
12 | let resistantFrom = damageRelations.defense.half;
13 | let weakFrom = damageRelations.defense.double;
14 |
15 | immuneFrom.forEach((type) => {
16 | multipliers[type] = multipliers.hasOwnProperty(type)
17 | ? multipliers[type] * 0
18 | : 0;
19 | });
20 |
21 | resistantFrom.forEach((type) => {
22 | multipliers[type] = multipliers.hasOwnProperty(type)
23 | ? multipliers[type] * 0.5
24 | : 0.5;
25 | });
26 |
27 | weakFrom.forEach((type) => {
28 | multipliers[type] = multipliers.hasOwnProperty(type)
29 | ? multipliers[type] * 2
30 | : 2;
31 | });
32 | });
33 |
34 | const matchups = {
35 | immuneTo: [],
36 | resistantTo: [],
37 | weakTo: [],
38 | };
39 |
40 | Object.entries(multipliers).forEach((category) => {
41 | const [type, multipler] = category;
42 |
43 | if (multipler === 0) matchups.immuneTo.push({ [type]: multipler });
44 | else if (multipler < 1) matchups.resistantTo.push({ [type]: multipler });
45 | else if (multipler > 1) matchups.weakTo.push({ [type]: multipler });
46 | });
47 |
48 | return matchups;
49 | };
50 |
51 | export default getMatchups;
52 |
--------------------------------------------------------------------------------
/styles/Matchup.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | flex: 1;
3 | margin-left: 1rem;
4 | }
5 |
6 | .header {
7 | color: #FFF;
8 | font-size: 2rem;
9 | font-weight: 800;
10 | letter-spacing: 0.1rem;
11 | text-shadow: 0 0 10px rgba(0, 0, 0, .5);
12 | }
13 |
14 | .card {
15 | margin: 3rem 0;
16 | padding: 1rem 1.5rem;
17 |
18 | min-height: 25rem;
19 | width: 100%;
20 |
21 | font-weight: 600;
22 | letter-spacing: 0.05rem;
23 | color: #000;
24 |
25 | border-radius: 10px;
26 |
27 | background-color: rgba(255, 255, 255, 0.5);
28 | box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px,
29 | rgba(0, 0, 0, 0.23) 0px 6px 6px;
30 |
31 | display: flex;
32 | flex-direction: column;
33 | }
34 |
35 | .category {
36 | font-size: 1.2rem;
37 | color: #404040;
38 | }
39 |
40 | .types {
41 | width: 100%;
42 | margin: 1rem 0;
43 |
44 | display: grid;
45 | grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
46 | }
47 |
48 | .typeContainer>button {
49 | height: 40px;
50 | width: 40px;
51 |
52 | display: flex;
53 | align-items: center;
54 | }
55 |
56 | .typeValue {
57 | position: relative;
58 | top: -37.5px;
59 | left: 0.9rem;
60 |
61 | height: 35px;
62 | width: 4.5rem;
63 |
64 | border-radius: 20px;
65 |
66 | color: #fff;
67 | font-size: 0.6rem;
68 | font-weight: 300;
69 | background-color: #404040;
70 |
71 | display: flex;
72 | justify-content: flex-end;
73 | align-items: center;
74 | }
75 |
76 | @media only screen and (max-width: 1000px) {
77 | .container {
78 | margin-left: 0;
79 | }
80 | }
--------------------------------------------------------------------------------
/styles/Stats.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | flex: 2;
3 | margin-right: 1rem;
4 | }
5 |
6 | .header {
7 | color: #FFF;
8 | font-size: 2rem;
9 | font-weight: 800;
10 | letter-spacing: 0.1rem;
11 | text-shadow: 0 0 10px rgba(0, 0, 0, .5);
12 | }
13 |
14 | .card {
15 | margin: 3rem 0;
16 | margin-right: 0rem;
17 | padding: 1rem 3.5rem;
18 |
19 | min-height: 18rem;
20 | width: 100%;
21 |
22 | font-weight: 600;
23 | letter-spacing: 0.05rem;
24 | color: #000;
25 |
26 | border-radius: 10px;
27 |
28 | background-color: rgba(255, 255, 255, 0.5);
29 | box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px,
30 | rgba(0, 0, 0, 0.23) 0px 6px 6px;
31 |
32 | display: flex;
33 | flex-direction: column;
34 | justify-content: center;
35 | }
36 |
37 | .row {
38 | margin: 0.5rem 0;
39 | width: 100%;
40 |
41 | display: flex;
42 | }
43 |
44 | .label {
45 | width: 5.5rem;
46 |
47 | color: #404040;
48 | display: inline-block;
49 | }
50 |
51 | .value {
52 | margin: 0 1rem;
53 | width: 2.5rem;
54 | color: #666666;
55 | }
56 |
57 | .total {
58 | font-weight: 700;
59 | color: #545454;
60 | }
61 |
62 | .chartContainer {
63 | /* height: 1rem; */
64 | width: 100%;
65 |
66 | display: flex;
67 | align-items: center;
68 | }
69 |
70 | .chart {
71 | height: 0.8rem;
72 | border-radius: 4px;
73 | border: 1px solid #000;
74 | /* filter: saturate(150%); */
75 | }
76 |
77 | @media only screen and (max-width: 1000px) {
78 | .container {
79 | margin-right: 0;
80 | }
81 |
82 | .card {
83 | padding: 1rem 1.5rem;
84 | }
85 |
86 | .value {
87 | margin: 0 0.25rem;
88 | }
89 | }
--------------------------------------------------------------------------------
/components/MatchupCard/index.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | import Type from '../Type';
4 | import getMatchups from '../../lib/matchups';
5 | import styles from '../../styles/Matchup.module.css';
6 |
7 | const TypeBadge = ({ name, multiplier }) => {
8 | let padding = '1rem';
9 | if (multiplier === 0.5) padding = '0.5rem';
10 | if (multiplier < 0.5 && multiplier !== 0) padding = '0.4rem';
11 |
12 | return (
13 |
14 |
15 |
16 | {multiplier}x
17 |
18 |
19 | );
20 | };
21 |
22 | const Category = ({ name, types }) => {
23 | return (
24 |
25 |
{name}
26 |
27 | {types.map((type, idx) => {
28 | let name = Object.keys(type)[0];
29 |
30 | return (
31 |
32 | );
33 | })}
34 |
35 |
36 | );
37 | };
38 |
39 | const MatchupCard = ({ types }) => {
40 | const [matchups, setMatchups] = useState(getMatchups(types));
41 |
42 | return (
43 |
44 |
Type Matchups
45 |
46 |
47 | {matchups.immuneTo.length !== 0 && (
48 |
49 | )}
50 |
51 | {matchups.resistantTo.length !== 0 && (
52 |
53 | )}
54 |
55 | {matchups.weakTo.length !== 0 && (
56 |
57 | )}
58 |
59 |
60 | );
61 | };
62 |
63 | export default MatchupCard;
64 |
--------------------------------------------------------------------------------
/components/Navbar/index.jsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 | import { useRouter } from 'next/router';
3 | import { useEffect, useState } from 'react';
4 |
5 | import { BsChevronLeft, BsChevronRight } from 'react-icons/bs';
6 | import styles from '../../styles/Navbar.module.css';
7 |
8 | const Navbar = () => {
9 | const router = useRouter();
10 | const current = Number(router.query.number);
11 |
12 | const generateLower = (num) => {
13 | let lower = num - 4;
14 |
15 | if (num - 4 < 0) lower = 1;
16 | if (num + 5 > 898) lower -= num + 5 - 898;
17 |
18 | return lower;
19 | };
20 |
21 | const [lower, setLower] = useState(generateLower(current));
22 | const [links, setLinks] = useState([]);
23 |
24 | useEffect(() => {
25 | setLower(generateLower(current));
26 | }, [current]);
27 |
28 | useEffect(() => {
29 | const nums = new Array(10);
30 | for (let i = 0; i != 10; i++) nums[i] = lower + i;
31 |
32 | setLinks(nums);
33 | }, [router.query.number, lower]);
34 |
35 | const handleLeft = () => {
36 | if (lower < 2) return;
37 | setLower((lwr) => lwr - 1);
38 | };
39 |
40 | const handleRight = () => {
41 | if (lower + 10 > 898) return;
42 | setLower((lwr) => lwr + 1);
43 | };
44 |
45 | return (
46 |
63 | );
64 | };
65 |
66 | export default Navbar;
67 |
--------------------------------------------------------------------------------
/components/EvolutionCard/index.jsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 | import Image from 'next/image';
3 |
4 | import { BsChevronRight } from 'react-icons/bs';
5 | import { getFrontSprite } from '../../lib/pokemon';
6 | import styles from '../../styles/Evolution.module.css';
7 |
8 | const PokemonIcon = ({ name, id, multi, borderColor }) => {
9 | const borderStyle = { outline: `4px solid ${borderColor}` };
10 |
11 | return (
12 |
16 |
17 |
18 |
{name}
19 |
20 |
21 | );
22 | };
23 |
24 | const EvolutionLine = ({ chain, color }) => {
25 | return (
26 |
27 | {chain.hasEvolved &&
}
28 |
2}
32 | borderColor={color}
33 | />
34 |
35 | {chain.evolvesTo.length <= 2 && (
36 |
37 | {chain.evolvesTo.map((next) => (
38 |
39 | ))}
40 |
41 | )}
42 |
43 | {chain.evolvesTo.length > 2 && (
44 |
45 |
46 |
47 | )}
48 |
49 | );
50 | };
51 |
52 | const MultiEvolution = ({ chain, color }) => {
53 | return (
54 | <>
55 |
56 |
57 |
58 | {chain.evolvesTo.map((next) => (
59 |
66 | ))}
67 |
68 | >
69 | );
70 | };
71 |
72 | const EvolutionCard = ({ chain, color }) => {
73 | return (
74 | <>
75 | Evolution Chain
76 |
77 |
78 |
79 |
80 | >
81 | );
82 | };
83 |
84 | export default EvolutionCard;
85 |
--------------------------------------------------------------------------------
/public/assets/types/dragon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/styles/Pokemon.module.css:
--------------------------------------------------------------------------------
1 | .main {
2 | height: 100%;
3 | padding: 4rem 8rem;
4 | padding-bottom: 2rem;
5 |
6 | transition: background-color 700ms ease;
7 | }
8 |
9 | .row {
10 | height: 100%;
11 |
12 | display: flex;
13 | justify-content: space-between;
14 | }
15 |
16 | .meta {
17 | color: #FFF;
18 | font-weight: 800;
19 | text-shadow: 0 0 10px rgba(0, 0, 0, .25);
20 | }
21 |
22 | .dexId {
23 | font-size: 1.2rem;
24 | }
25 |
26 | .name {
27 | font-size: 2.7rem;
28 | }
29 |
30 | .category {
31 | font-size: 1.1rem;
32 | }
33 |
34 | .types {
35 | margin: 3rem 0;
36 | display: flex;
37 | }
38 |
39 | .art {
40 | margin-top: 3rem;
41 | }
42 |
43 | .image {
44 | filter: drop-shadow(2px 2px 5px black);
45 | }
46 |
47 | .card {
48 | margin: 5rem 0;
49 | padding: 1rem 1.5rem;
50 |
51 | /* height: 15rem; */
52 | width: 25rem;
53 |
54 | font-weight: 600;
55 | letter-spacing: 0.05rem;
56 | color: #000;
57 |
58 | border-radius: 10px;
59 |
60 | opacity: 0.5;
61 | background-color: #fff;
62 | box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px,
63 | rgba(0, 0, 0, 0.23) 0px 6px 6px;
64 |
65 | display: flex;
66 | justify-content: space-evenly;
67 | }
68 |
69 | .card div {
70 | margin: 0.3rem;
71 | }
72 |
73 | .key {
74 | margin-bottom: 0.5rem;
75 | margin-right: 0.4rem;
76 |
77 | font-size: 0.95rem;
78 | color: #737373;
79 | }
80 |
81 | .ability {
82 | margin: 0.3rem 0;
83 | display: block;
84 | /* white-space: pre-wrap; */
85 | }
86 |
87 | .abilityHidden {
88 | letter-spacing: 0;
89 | font-size: 0.7rem;
90 | }
91 |
92 | .arrow {
93 | position: absolute;
94 | bottom: 2rem;
95 | left: 50%;
96 |
97 | font-size: 3rem;
98 | color: #fff;
99 |
100 | filter: drop-shadow(3px 3px 2px rgba(0, 0, 0, .7));
101 | }
102 |
103 |
104 | @media only screen and (max-width: 768px) {
105 | .main {
106 | padding: 3rem 1.5rem;
107 | padding-bottom: 0;
108 | }
109 |
110 | .art {
111 | top: 0;
112 | margin-top: 12rem;
113 | position: absolute;
114 | }
115 |
116 | .identifier {
117 | display: flex;
118 | justify-content: space-between;
119 | }
120 |
121 | .types {
122 | margin: 1.5rem 0;
123 | }
124 |
125 | .types>button {
126 | margin: 0 0.4rem;
127 | }
128 |
129 | .container {
130 | margin-top: 125%;
131 | }
132 |
133 | .row {
134 | display: block;
135 | }
136 |
137 | .card,
138 | .main,
139 | .row {
140 | width: 100%;
141 | }
142 | }
--------------------------------------------------------------------------------
/lib/pokemon.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const getOfficialArtwork = (number) =>
4 | `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${number}.png`;
5 |
6 | export const getFrontSprite = (number) =>
7 | `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${number}.png`;
8 |
9 | export const getHomeSprite = (number) =>
10 | `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home/${number}.png`;
11 |
12 | const getPokemon = async (number) => {
13 | const { data } = await axios.get(
14 | `https://pokeapi.co/api/v2/pokemon/${Number(number)}`
15 | );
16 |
17 | return data;
18 | };
19 |
20 | const getGenus = async (number) => {
21 | const { data } = await axios.get(
22 | `https://pokeapi.co/api/v2/pokemon-species/${Number(number)}`
23 | );
24 |
25 | return data;
26 | };
27 |
28 | const getEvolution = async (uri) => {
29 | const { data } = await axios.get(uri);
30 |
31 | const getName = (name) =>
32 | name
33 | .split('-')
34 | .map((word) => word[0].toUpperCase() + word.slice(1))
35 | .join(' ');
36 |
37 | const getId = (uri) => {
38 | let uriArray = uri.split('/');
39 | const number = Number(uriArray[uriArray.length - 2]);
40 | return number;
41 | };
42 |
43 | const populateEvolutions = (chain) => {
44 | if (!chain.evolves_to) return;
45 | else
46 | return {
47 | name: getName(chain.species.name),
48 | id: getId(chain.species.url),
49 | isBaby: chain.is_baby,
50 | hasEvolved: true,
51 |
52 | evolvesTo: [
53 | ...chain.evolves_to.map((evolution) =>
54 | populateEvolutions(evolution)
55 | ),
56 | ],
57 | };
58 | };
59 |
60 | const chain = {
61 | name: getName(data.chain.species.name),
62 | id: getId(data.chain.species.url),
63 | isBaby: data.chain.is_baby,
64 | hasEvolved: false,
65 |
66 | evolvesTo: [
67 | ...data.chain.evolves_to.map((evo) => populateEvolutions(evo)),
68 | ],
69 | };
70 |
71 | return chain;
72 | };
73 |
74 | export const getPokemonData = async (number) => {
75 | const pokemonData = await getPokemon(number);
76 | const speciesData = await getGenus(number);
77 | const evolutionData = await getEvolution(speciesData.evolution_chain.url);
78 |
79 | const genus = speciesData.genera.filter((genera) => {
80 | if (genera.language.name === 'en') return genera.genus;
81 | })[0].genus;
82 |
83 | const pokemon = {
84 | id: pokemonData.id,
85 | name: pokemonData.name,
86 | species: { name: pokemonData.species.name },
87 | category: genus,
88 |
89 | height: pokemonData.height,
90 | weight: pokemonData.weight,
91 |
92 | abilities: pokemonData.abilities,
93 | stats: pokemonData.stats,
94 | type: pokemonData.types,
95 |
96 | sprites: {
97 | front_default: getFrontSprite(number),
98 | home_front: getHomeSprite(number),
99 | official_artwork: getOfficialArtwork(number),
100 | },
101 |
102 | evolutionChain: evolutionData,
103 | };
104 |
105 | return pokemon;
106 | };
107 |
--------------------------------------------------------------------------------
/styles/Evolution.module.css:
--------------------------------------------------------------------------------
1 | .header {
2 | color: #FFF;
3 | font-size: 2rem;
4 | font-weight: 800;
5 | letter-spacing: 0.1rem;
6 | text-shadow: 0 0 10px rgba(0, 0, 0, .5);
7 | }
8 |
9 | .card {
10 | margin: 3rem 0;
11 | padding: 1rem 1.5rem;
12 |
13 | min-height: 25rem;
14 | width: 100%;
15 |
16 | font-weight: 600;
17 | letter-spacing: 0.05rem;
18 | color: #000;
19 |
20 | border-radius: 10px;
21 |
22 | background-color: rgba(255, 255, 255, 0.5);
23 | box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px,
24 | rgba(0, 0, 0, 0.23) 0px 6px 6px;
25 |
26 | display: flex;
27 | justify-content: center;
28 | align-items: center;
29 | }
30 |
31 | .group {
32 | width: 100%;
33 |
34 | display: flex;
35 | justify-content: center;
36 | align-items: center;
37 | }
38 |
39 | .group>svg,
40 | .multiGroup>svg {
41 | margin-right: 1rem;
42 | font-size: 2rem;
43 | color: #fff;
44 | filter: drop-shadow(1px 3px 1px rgb(0, 0, 0, 0.5));
45 | }
46 |
47 | .stage {
48 | margin-left: 2rem;
49 | }
50 |
51 | .multiGroup {
52 | display: flex;
53 | justify-content: center;
54 | align-items: center;
55 | }
56 |
57 | .multiStage {
58 | width: 80%;
59 |
60 | display: flex;
61 | align-items: center;
62 |
63 | flex-wrap: wrap;
64 | }
65 |
66 | .multiStage>div {
67 | /* height: 125px;
68 | width: 125px; */
69 |
70 | flex-basis: 20%;
71 | }
72 |
73 | .pokemon {
74 | height: 150px;
75 | width: 150px;
76 | margin: 1rem 0;
77 |
78 | background-color: #fff;
79 | border-radius: 50%;
80 |
81 | display: flex;
82 | flex-direction: column;
83 | justify-content: center;
84 | align-items: center;
85 | }
86 |
87 | .pokemon img:hover {
88 | transform: translateY(-1px);
89 | }
90 |
91 | .pokemon>a {
92 | color: rgba(0, 0, 0, .7);
93 |
94 | font-size: 0.8rem;
95 | font-family: 'Nunito Sans';
96 | font-weight: 800;
97 |
98 | margin-bottom: 0.7rem;
99 | }
100 |
101 | .pokemon>a:hover {
102 | text-decoration: underline;
103 | text-decoration-thickness: 2px;
104 | }
105 |
106 | .multiPokemon {
107 | margin-left: 2rem;
108 | flex: 0 0 auto;
109 | }
110 |
111 | @media only screen and (max-width: 1000px) {
112 | .group {
113 | flex-direction: column;
114 | margin: 0 0.5rem;
115 | }
116 |
117 | .stage {
118 | margin-left: 0;
119 | display: flex;
120 | }
121 |
122 | .stage .pokemon {
123 | height: 125px;
124 | width: 125px;
125 | }
126 |
127 | .group>svg,
128 | .multiGroup>svg {
129 | margin-right: 0;
130 | transform: rotate(90deg);
131 | }
132 |
133 | .multiPokemon {
134 | margin-left: 0;
135 | }
136 |
137 | .multiGroup {
138 | flex-direction: column;
139 | }
140 |
141 | .multiStage {
142 | width: 100%;
143 | justify-content: space-evenly;
144 | }
145 |
146 | .multiStage>div {
147 | /* margin: 0.5rem 0.5rem; */
148 | height: 125px;
149 | width: 125px;
150 |
151 | flex-basis: 40%;
152 | }
153 | }
--------------------------------------------------------------------------------
/components/StatsCard/index.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import styles from '../../styles/Stats.module.css';
3 |
4 | const StatsCard = ({ stats }) => {
5 | const getTotalStats = (stats) => {
6 | let totalStats = 0;
7 | stats.forEach((stat) => {
8 | totalStats += stat.base_stat;
9 | });
10 | return totalStats;
11 | };
12 |
13 | const [totalStats, setTotalStats] = useState(getTotalStats(stats));
14 |
15 | return (
16 |
17 |
Base Stats
18 |
19 |
20 |
21 |
HP
22 |
{stats[0].base_stat}
23 |
24 |
33 |
34 |
35 |
Attack
36 |
{stats[1].base_stat}
37 |
38 |
47 |
48 |
49 |
Defense
50 |
{stats[2].base_stat}
51 |
52 |
61 |
62 |
63 |
Sp. Atk
64 |
{stats[3].base_stat}
65 |
66 |
75 |
76 |
77 |
Sp. Def
78 |
{stats[4].base_stat}
79 |
80 |
89 |
90 |
91 |
Speed
92 |
{stats[5].base_stat}
93 |
94 |
103 |
104 |
105 |
106 |
Total
107 |
108 | {totalStats}
109 |
110 |
111 |
112 |
113 |
114 |
115 | );
116 | };
117 |
118 | export default StatsCard;
119 |
--------------------------------------------------------------------------------
/lib/types.json:
--------------------------------------------------------------------------------
1 | {
2 | "bug": {
3 | "attack": {
4 | "double": ["psychic", "grass", "dark"],
5 | "half": [
6 | "fighting",
7 | "fire",
8 | "flying",
9 | "ghost",
10 | "poison",
11 | "steel",
12 | "fairy"
13 | ],
14 | "zero": []
15 | },
16 | "defense": {
17 | "half": ["fighting", "grass", "ground"],
18 | "double": ["fire", "flying", "rock"],
19 | "zero": []
20 | }
21 | },
22 | "dark": {
23 | "attack": {
24 | "double": ["ghost", "psychic"],
25 | "half": ["dark", "fighting", "fairy"],
26 | "zero": []
27 | },
28 | "defense": {
29 | "half": ["dark", "ghost"],
30 | "double": ["bug", "fighting", "fairy"],
31 | "zero": ["psychic"]
32 | }
33 | },
34 | "dragon": {
35 | "attack": {
36 | "double": ["dragon"],
37 | "half": ["steel"],
38 | "zero": ["fairy"]
39 | },
40 | "defense": {
41 | "half": ["electric", "fire", "grass", "water"],
42 | "double": ["dragon", "ice", "fairy"],
43 | "zero": []
44 | }
45 | },
46 | "electric": {
47 | "attack": {
48 | "double": ["flying", "water"],
49 | "half": ["dragon", "electric", "grass"],
50 | "zero": ["ground"]
51 | },
52 | "defense": {
53 | "half": ["electric", "flying", "steel"],
54 | "double": ["ground"],
55 | "zero": []
56 | }
57 | },
58 | "fairy": {
59 | "attack": {
60 | "double": ["dark", "dragon", "fighting"],
61 | "half": ["fire", "poison", "steel"],
62 | "zero": []
63 | },
64 | "defense": {
65 | "half": ["bug", "dark", "fighting"],
66 | "double": ["poison", "steel"],
67 | "zero": ["dragon"]
68 | }
69 | },
70 | "fighting": {
71 | "attack": {
72 | "double": ["dark", "ice", "normal", "rock", "steel"],
73 | "half": ["bug", "fairy", "flying", "poison", "psychic"],
74 | "zero": ["ghost"]
75 | },
76 | "defense": {
77 | "half": ["bug", "dark", "rock"],
78 | "double": ["fairy", "flying", "psychic"],
79 | "zero": []
80 | }
81 | },
82 | "fire": {
83 | "attack": {
84 | "double": ["bug", "grass", "ice", "steel"],
85 | "half": ["dragon", "fire", "rock", "water"],
86 | "zero": []
87 | },
88 | "defense": {
89 | "half": ["bug", "fairy", "fire", "grass", "ice", "steel"],
90 | "double": ["bug", "fire", "flying", "ice", "poison"],
91 | "zero": []
92 | }
93 | },
94 | "flying": {
95 | "attack": {
96 | "double": ["bug", "fighting", "grass"],
97 | "half": ["electric", "rock", "steel"],
98 | "zero": []
99 | },
100 | "defense": {
101 | "half": ["bug", "fighting", "grass"],
102 | "double": ["electric", "ice", "rock"],
103 | "zero": ["ground"]
104 | }
105 | },
106 | "ghost": {
107 | "attack": {
108 | "double": ["ghost", "psychic"],
109 | "half": ["dark"],
110 | "zero": ["normal"]
111 | },
112 | "defense": {
113 | "half": ["bug", "poison"],
114 | "double": ["ghost", "dark"],
115 | "zero": ["normal", "fighting"]
116 | }
117 | },
118 | "grass": {
119 | "attack": {
120 | "double": ["ground", "rock", "water"],
121 | "half": [
122 | "bug",
123 | "dragon",
124 | "fire",
125 | "flying",
126 | "grass",
127 | "poison",
128 | "steel"
129 | ],
130 | "zero": []
131 | },
132 | "defense": {
133 | "half": ["electric", "grass", "ground", "water"],
134 | "double": ["bug", "fire", "flying", "ice", "poison"],
135 | "zero": []
136 | }
137 | },
138 | "ground": {
139 | "attack": {
140 | "double": ["electric", "fire", "poison", "rock", "steel"],
141 | "half": ["bug", "grass"],
142 | "zero": ["flying"]
143 | },
144 | "defense": {
145 | "half": ["poison", "rock"],
146 | "double": ["grass", "ice", "water"],
147 | "zero": ["electric"]
148 | }
149 | },
150 | "ice": {
151 | "attack": {
152 | "double": ["dragon", "flying", "grass", "ground"],
153 | "half": ["fire", "ice", "steel", "water"],
154 | "zero": []
155 | },
156 | "defense": {
157 | "half": ["ice"],
158 | "double": ["fighting", "fire", "rock", "steel"],
159 | "zero": []
160 | }
161 | },
162 | "normal": {
163 | "attack": {
164 | "double": [],
165 | "half": ["rock", "steel"],
166 | "zero": ["ghost"]
167 | },
168 | "defense": {
169 | "half": [],
170 | "double": ["fighting"],
171 | "zero": ["ghost"]
172 | }
173 | },
174 | "poison": {
175 | "attack": {
176 | "double": ["grass", "fairy"],
177 | "half": ["ghost", "ground", "poison", "rock"],
178 | "zero": ["steel"]
179 | },
180 | "defense": {
181 | "half": ["bug", "fairy", "fighting", "grass", "poison"],
182 | "double": ["ground", "psychic"],
183 | "zero": []
184 | }
185 | },
186 | "psychic": {
187 | "attack": {
188 | "double": ["fighting", "poison"],
189 | "half": ["psychic", "steel"],
190 | "zero": ["dark"]
191 | },
192 | "defense": {
193 | "half": ["fighting", "psychic"],
194 | "double": ["bug", "dark", "ghost"],
195 | "zero": []
196 | }
197 | },
198 | "rock": {
199 | "attack": {
200 | "double": ["bug", "fire", "flying", "ice"],
201 | "half": ["fighting", "ground", "steel"],
202 | "zero": []
203 | },
204 | "defense": {
205 | "half": ["fire", "flying", "normal", "poison"],
206 | "double": ["fighting", "grass", "ground", "steel", "water"],
207 | "zero": []
208 | }
209 | },
210 | "steel": {
211 | "attack": {
212 | "double": ["fairy", "ice", "rock"],
213 | "half": ["electric", "fire", "steel", "water"],
214 | "zero": []
215 | },
216 | "defense": {
217 | "half": [
218 | "bug",
219 | "dragon",
220 | "fairy",
221 | "flying",
222 | "grass",
223 | "ice",
224 | "normal",
225 | "psychic",
226 | "rock",
227 | "steel"
228 | ],
229 | "double": ["fighting", "fire", "ground"],
230 | "zero": ["poison"]
231 | }
232 | },
233 | "water": {
234 | "attack": {
235 | "double": ["fire", "ground", "rock"],
236 | "half": ["dragon", "grass", "steel"],
237 | "zero": []
238 | },
239 | "defense": {
240 | "half": ["fire", "ice", "steel", "water"],
241 | "double": ["electric", "grass"],
242 | "zero": []
243 | }
244 | }
245 | }
246 |
--------------------------------------------------------------------------------
/pages/dex/[number].jsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import Image from 'next/image';
3 | import { useRouter } from 'next/router';
4 | import { useEffect, useState } from 'react';
5 |
6 | import tinycolor from 'tinycolor2';
7 | import { prominent } from 'color.js';
8 | import { useSwipeable } from 'react-swipeable';
9 |
10 | import Type from '../../components/Type';
11 | import Navbar from '../../components/Navbar';
12 | import EvolutionCard from '../../components/EvolutionCard';
13 | import StatsCard from '../../components/StatsCard';
14 | import MatchupCard from '../../components/MatchupCard';
15 |
16 | import { getHomeSprite, getPokemonData } from '../../lib/pokemon';
17 |
18 | import styles from '../../styles/Pokemon.module.css';
19 |
20 | const Pokemon = ({ pokemonData }) => {
21 | const router = useRouter();
22 |
23 | const getName = (name) => {
24 | let speciesName = name
25 | .split('-')
26 | .map((word) => word[0].toUpperCase() + word.slice(1))
27 | .join(' ');
28 | return speciesName;
29 | };
30 |
31 | const [name, setName] = useState(getName(pokemonData.species.name));
32 | const [dexNumber, setDexNumber] = useState(
33 | `#${String(router.query.number).padStart(3, '0')}`
34 | );
35 |
36 | const [abilities, setAbilities] = useState([]);
37 | const [vibrantColor, setVibrantColor] = useState('#FFFFFF');
38 |
39 | useEffect(() => {
40 | setDexNumber(`#${String(router.query.number).padStart(3, '0')}`);
41 | }, [router.query.number]);
42 |
43 | useEffect(() => {
44 | const getPalette = async () => {
45 | const promColor = await prominent(pokemonData.sprites.home_front, {
46 | amount: 3,
47 | group: 30,
48 | format: 'hex',
49 | });
50 |
51 | let bgColor = tinycolor(promColor[1]).isLight()
52 | ? promColor[2]
53 | : promColor[1];
54 |
55 | setVibrantColor(bgColor);
56 | document.body.style.backgroundColor = bgColor;
57 | };
58 | getPalette();
59 | }, [pokemonData]);
60 |
61 | useEffect(() => {
62 | setAbilities(
63 | pokemonData.abilities.map((ability) => ({
64 | name: ability.ability.name
65 | .split('-')
66 | .map((word) => word[0].toUpperCase() + word.slice(1))
67 | .join(' '),
68 | isHidden: ability.is_hidden,
69 | }))
70 | );
71 |
72 | setName(getName(pokemonData.species.name));
73 | }, [pokemonData]);
74 |
75 | const handlePrev = () => {
76 | if (Number(router.query.number) === 1) return;
77 | else router.push(`/dex/${Number(router.query.number) - 1}`);
78 | };
79 |
80 | const handleNext = () => {
81 | if (Number(router.query.number) === 898) return;
82 | else router.push(`/dex/${Number(router.query.number) + 1}`);
83 | };
84 |
85 | const handlers = useSwipeable({
86 | onSwipedRight: () => handlePrev(),
87 | onSwipedLeft: () => handleNext(),
88 |
89 | delta: 170,
90 | preventDefaultTouchmoveEvent: false,
91 | trackTouch: true,
92 | trackMouse: false,
93 | rotationAngle: 0,
94 | });
95 |
96 | return (
97 | <>
98 |
99 |
104 |
105 | {dexNumber} | {name} | ToxaDex
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
{dexNumber}
118 |
{name}
119 |
120 | {pokemonData.category}
121 |
122 |
123 |
124 |
125 | {pokemonData.type.map((type, idx) => (
126 |
127 | ))}
128 |
129 |
130 |
131 |
132 |
133 |
134 |
Height{' '}
135 |
{pokemonData.height / 10}m
136 |
137 |
138 |
Weight{' '}
139 |
{pokemonData.weight / 10}kg
140 |
141 |
142 |
Abilities{' '}
143 |
144 | {abilities.map((ability, idx) => (
145 |
146 | {ability.name}{' '}
147 | {ability.isHidden && (
148 |
151 | (Hidden)
152 |
153 | )}
154 |
155 | ))}
156 |
157 |
158 |
159 |
160 |
161 |
162 |
173 |
174 |
175 |
176 |
177 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 | >
191 | );
192 | };
193 |
194 | export const getStaticPaths = () => {
195 | const paths = [...Array(898).keys()].map((_, id) => {
196 | return { params: { number: String(id + 1) } };
197 | });
198 |
199 | return {
200 | paths,
201 | fallback: false,
202 | };
203 | };
204 |
205 | export const getStaticProps = async ({ params }) => {
206 | const pokemonData = await getPokemonData(params.number);
207 |
208 | return {
209 | props: { pokemonData },
210 | };
211 | };
212 |
213 | export default Pokemon;
214 |
--------------------------------------------------------------------------------
/styles/Type.module.css:
--------------------------------------------------------------------------------
1 | .type {
2 | margin: 0 0.6rem;
3 | height: 64px;
4 | width: 64px;
5 |
6 | background-color: #fff;
7 | border-radius: 50%;
8 |
9 | z-index: 1;
10 | border: none;
11 | }
12 |
13 | .type:hover {
14 | filter: saturate(200%);
15 | }
16 |
17 | .type:focus {
18 | outline: none;
19 | }
20 |
21 | .label {
22 | position: relative;
23 | color: #fff;
24 |
25 | transition: all .25s ease-in-out;
26 | cursor: pointer;
27 | }
28 |
29 | .label:before {
30 | position: absolute;
31 | content: attr(data-tip);
32 |
33 | min-width: 2rem;
34 | padding: 0.3rem 0.6rem;
35 |
36 | color: inherit;
37 | font-size: 0.6rem;
38 | font-weight: 800;
39 | text-align: center;
40 | letter-spacing: 0;
41 | text-transform: uppercase;
42 |
43 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
44 |
45 | opacity: 0;
46 | visibility: hidden;
47 |
48 | transition: all .3s ease-in-out;
49 | }
50 |
51 | .label:hover:before,
52 | .label:hover:after {
53 | visibility: visible;
54 | opacity: 1
55 | }
56 |
57 | .label.tooltip:before {
58 | background-color: inherit;
59 |
60 | top: 0;
61 | left: 50%;
62 |
63 | border-radius: 5px;
64 | transform: translate(-50%, calc(-100% - 5px));
65 | }
66 |
67 | .bug {
68 | background-color: #92BC2C;
69 |
70 | background-image: url('../public/assets/types/bug.svg');
71 | background-position: 50%;
72 | background-size: 50%;
73 | background-repeat: no-repeat;
74 |
75 | box-shadow: 0 0 20px #92BC2C;
76 | }
77 |
78 | .dark {
79 | background-color: #595761;
80 |
81 | background-image: url('../public/assets/types/dark.svg');
82 | background-position: 50%;
83 | background-size: 50%;
84 | background-repeat: no-repeat;
85 |
86 | box-shadow: 0 0 20px #595761;
87 | }
88 |
89 | .dragon {
90 | background-color: #0C69C8;
91 |
92 | background-image: url('../public/assets/types/dragon.svg');
93 | background-position: 50%;
94 | background-size: 50%;
95 | background-repeat: no-repeat;
96 |
97 | box-shadow: 0 0 20px #0C69C8;
98 | }
99 |
100 | .electric {
101 | background-color: #F2D94E;
102 |
103 | background-image: url('../public/assets/types/electric.svg');
104 | background-position: 50%;
105 | background-size: 50%;
106 | background-repeat: no-repeat;
107 |
108 | box-shadow: 0 0 20px #F2D94E;
109 | }
110 |
111 | .fire {
112 | background-color: #FBA54C;
113 |
114 | background-image: url('../public/assets/types/fire.svg');
115 | background-position: 50%;
116 | background-size: 50%;
117 | background-repeat: no-repeat;
118 |
119 | box-shadow: 0 0 20px #FBA54C;
120 | }
121 |
122 | .fairy {
123 | background-color: #EE90E6;
124 |
125 | background-image: url('../public/assets/types/fairy.svg');
126 | background-position: 50%;
127 | background-size: 50%;
128 | background-repeat: no-repeat;
129 |
130 | box-shadow: 0 0 20px #EE90E6;
131 | }
132 |
133 | .fighting {
134 | background-color: #D3425F;
135 |
136 | background-image: url('../public/assets/types/fighting.svg');
137 | background-position: 50%;
138 | background-size: 50%;
139 | background-repeat: no-repeat;
140 |
141 | box-shadow: 0 0 20px #D3425F;
142 | }
143 |
144 | .flying {
145 | background-color: #A1BBEC;
146 |
147 | background-image: url('../public/assets/types/flying.svg');
148 | background-position: 50%;
149 | background-size: 50%;
150 | background-repeat: no-repeat;
151 |
152 | box-shadow: 0 0 20px #A1BBEC;
153 | }
154 |
155 | .ghost {
156 | background-color: #5F6DBC;
157 |
158 | background-image: url('../public/assets/types/ghost.svg');
159 | background-position: 50%;
160 | background-size: 50%;
161 | background-repeat: no-repeat;
162 |
163 | box-shadow: 0 0 20px #5F6DBC;
164 | }
165 |
166 | .grass {
167 | background-color: #5FBD58;
168 |
169 | background-image: url('../public/assets/types/grass.svg');
170 | background-position: 50%;
171 | background-size: 50%;
172 | background-repeat: no-repeat;
173 |
174 | box-shadow: 0 0 20px #5FBD58;
175 | }
176 |
177 | .ground {
178 | background-color: #DA7C4D;
179 |
180 | background-image: url('../public/assets/types/ground.svg');
181 | background-position: 50%;
182 | background-size: 50%;
183 | background-repeat: no-repeat;
184 |
185 | box-shadow: 0 0 20px #DA7C4D;
186 | }
187 |
188 | .ice {
189 | background-color: #75D0C1;
190 |
191 | background-image: url('../public/assets/types/ice.svg');
192 | background-position: 50%;
193 | background-size: 50%;
194 | background-repeat: no-repeat;
195 |
196 | box-shadow: 0 0 20px #75D0C1;
197 | }
198 |
199 | .normal {
200 | background-color: #A0A29F;
201 |
202 | background-image: url('../public/assets/types/normal.svg');
203 | background-position: 50%;
204 | background-size: 50%;
205 | background-repeat: no-repeat;
206 |
207 | box-shadow: 0 0 20px #A0A29F;
208 | }
209 |
210 | .poison {
211 | background-color: #B763CF;
212 |
213 | background-image: url('../public/assets/types/poison.svg');
214 | background-position: 50%;
215 | background-size: 50%;
216 | background-repeat: no-repeat;
217 |
218 | box-shadow: 0 0 20px #B763CF;
219 | }
220 |
221 | .psychic {
222 | background-color: #FA8581;
223 |
224 | background-image: url('../public/assets/types/psychic.svg');
225 | background-position: 50%;
226 | background-size: 50%;
227 | background-repeat: no-repeat;
228 |
229 | box-shadow: 0 0 20px #FA8581;
230 | }
231 |
232 | .rock {
233 | background-color: #C9BB8A;
234 |
235 | background-image: url('../public/assets/types/rock.svg');
236 | background-position: 50%;
237 | background-size: 50%;
238 | background-repeat: no-repeat;
239 |
240 | box-shadow: 0 0 20px #C9BB8A;
241 | }
242 |
243 | .steel {
244 | background-color: #5695A3;
245 |
246 | background-image: url('../public/assets/types/steel.svg');
247 | background-position: 50%;
248 | background-size: 50%;
249 | background-repeat: no-repeat;
250 |
251 | box-shadow: 0 0 20px #5695A3;
252 | }
253 |
254 | .water {
255 | background-color: #539DDF;
256 |
257 | background-image: url('../public/assets/types/water.svg');
258 | background-position: 50%;
259 | background-size: 50%;
260 | background-repeat: no-repeat;
261 |
262 | box-shadow: 0 0 20px #539DDF;
263 | }
264 |
265 | @media only screen and (max-width: 992px) {
266 | .type:focus {
267 | filter: saturate(200%);
268 | }
269 |
270 | .label:focus:before,
271 | .label:focus:after {
272 | visibility: visible;
273 | opacity: 1
274 | }
275 | }
--------------------------------------------------------------------------------