feat(page): set up home (showcase, faq) & components

This commit is contained in:
Ash Monty
2022-11-27 12:34:00 +01:00
parent 96661adc76
commit 4ef1c4f0ff
16 changed files with 1211 additions and 42 deletions

44
components/Faq/Faq.js Normal file
View File

@@ -0,0 +1,44 @@
import { useState } from 'react';
import classNames from 'classnames';
import { CaretRight } from 'phosphor-react';
import styles from './Faq.module.css';
export default function Faq(ctx) {
const { questionObject } = ctx;
const [selectedIndex, setSelectedIndex] = useState(0);
const questions = {
odd: questionObject.filter((_, i) => i % 2 === 0),
even: questionObject.filter((_, i) => i % 2 !== 0),
};
// reusable question component
const QnA = ({ question, i, selected }) => (
<div className={styles.question}>
<h3 className={classNames({ [styles.selected]: selected })} onClick={() => setSelectedIndex(i)}>
<CaretRight size={24} weight="bold" className={styles.chevron} />
{question.question}
</h3>
<p style={{ display: selected ? 'block' : 'none' }} dangerouslySetInnerHTML={{ __html: question.answer }} />
</div>
);
return (
<div className={styles.faq}>
<div>
{questions.odd.map((question, i) => {
const selected = selectedIndex === i;
return <QnA key={i} question={question} selected={selected} i={i} />;
})}
</div>
<div>
{questions.even.map((question, i) => {
const index = i + questions.odd.length;
const selected = selectedIndex === index;
return <QnA key={i} question={question} selected={selected} i={index} />;
})}
</div>
</div>
);
}

View File

@@ -0,0 +1,45 @@
.faq {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
margin: 2rem 0;
}
.question h3 {
font-size: 1.5rem;
font-weight: 300;
margin: 0;
margin-bottom: 1rem;
color: var(--text-shade-2);
cursor: pointer;
}
.question h3:hover {
color: var(--text-shade-3);
text-decoration: underline;
}
.question h3.selected {
color: var(--text-shade-3);
}
.question p {
overflow-y: hidden;
}
.chevron {
position: relative;
top: 2px;
margin-left: -6px;
}
.selected .chevron {
transform: rotate(90deg);
margin-right: 4px;
}
@media screen and (max-width: 840px) {
.faq {
grid-template-columns: 1fr;
gap: 0;
}
}

View File

@@ -0,0 +1,28 @@
import classNames from 'classnames';
import styles from './Section.module.css';
/**
* A reusable component for sections.
*
* @param {import('react').ComponentClass} className - Custom classes to apply to the section.
* @param {import('react').ComponentClass} contentClassName - Custom styles to apply to the content.
* @param {string} style - Custom styles to apply to the section.
* @param {string} id - The id of the section.
*
* @example
* <Section className={styles.showcaseTail} contentClassName={styles.content}>
* <Title>Hello</Title>
* </Section>
*
*/
export default function Section(ctx) {
const { id, style, children, className, contentClassName } = ctx;
return (
<section className={classNames(styles.sectionWrapper, className)} id={id} style={style}>
<div className={classNames(styles.content, contentClassName)}>{children}</div>
</section>
);
}

View File

@@ -0,0 +1,16 @@
.sectionWrapper {
display: flex;
padding: 180px 0;
}
.sectionWrapper .content {
width: 100%;
max-width: var(--maxwidth);
margin: auto;
}
@media screen and (max-width: 840px) {
.sectionWrapper {
padding: 90px 0;
}
}

View File

@@ -0,0 +1,23 @@
import styles from './ShowcaseSection.module.css';
import classNames from 'classnames';
import Image from 'next/image';
import Section from '../Section/Section';
import Title from '../Title/Title';
export default function ShowcaseSection(ctx) {
const { title, caption, image, isOdd } = ctx;
return (
<Section
className={classNames(styles.sectionWrapper, { [styles.odd]: isOdd })}
contentClassName={styles.content}
>
<div className={styles.text}>
<Title>{title}</Title>
<p className={styles.caption}>{caption}</p>
</div>
<Image src={image} alt="" className={styles.image} quality={100} sizes="(max-width: 840px) 100vw, 700px" />
</Section>
);
}

View File

@@ -0,0 +1,55 @@
.content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: min(10vw, 200px);
justify-content: space-between;
align-items: center;
width: 100%;
max-width: var(--maxwidth);
margin: auto;
}
.image {
width: 100%;
height: auto;
max-width: 600px;
}
.odd.sectionWrapper {
background: var(--bg-shade-0);
}
.odd .text {
grid-column: 2;
}
.odd .image {
grid-column: 1;
grid-row: 1;
}
@media screen and (max-width: 840px) {
.content {
grid-template-columns: 1fr;
gap: 42px;
}
.text,
.image,
.odd .text,
.odd .image {
grid-column: 1;
}
.text,
.odd .text {
grid-row: 1;
}
.image,
.odd .image {
margin: auto;
grid-row: 2;
}
}

View File

@@ -79,24 +79,23 @@
}
]
},
"showcase": {
"title": "What we make",
"text": "Our project has many components. Here are some of them.",
"cards": [
{
"title": "Game servers",
"caption": "Bringing back your favorite games and content using custom servers."
},
{
"title": "Juxtaposition",
"caption": "A re-imagining of Miiverse, as if it were made in the modern era."
},
{
"title": "Cemu support",
"caption": "Play your favorite Wii U titles even without a console!"
}
]
},
"showcase" : [
{
"title": "Online play.",
"caption": "Dash down Mount Wario on Wii U, [some 3DS stuff we have implemented], upload a course to Mario Maker on CEMU and more, online with Pretendo Network!",
"image": "network"
},
{
"title": "Juxtaposition.",
"caption": "Juxtaposition is our reimagining of Miiverse for the modern era. With a refreshingly modern design and in-game posting support, insert text insert text insert text insert text insert text!",
"image": "juxt"
},
{
"title": "Emulators.",
"caption": "See you later, emulator! I wish I had something to put here instead of dummy text, and yet...",
"image": "pcmouse"
}
],
"credits": {
"title": "The team",
"text": "Meet the team behind the project",

845
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
"name": "pretendo",
"version": "2.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
@@ -9,10 +10,17 @@
"lint": "next lint"
},
"dependencies": {
"@next/font": "^13.0.1",
"chalk": "^5.1.2",
"classnames": "^2.3.2",
"eslint": "8.26.0",
"eslint-config-next": "13.0.1",
"fs-extra": "^10.1.0",
"lodash.merge": "^4.6.2",
"next": "13.0.1",
"phosphor-react": "^1.4.1",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"sharp": "^0.31.2"
}
}

View File

@@ -1,3 +1,85 @@
export default function Home() {
return <p>bandwidth my beloved</p>;
import { getLocale } from '../utils/locale';
import Button from '../components/Button/Button';
import Title from '../components/Title/Title';
import Section from '../components/Section/Section';
import ShowcaseSection from '../components/ShowcaseSection/ShowcaseSection';
import Faq from '../components/Faq/Faq';
import Image from 'next/image';
import styles from './index.module.css';
import { useRouter } from 'next/router';
import juxtImage from '../public/assets/images/showcase/juxt.png';
import networkImage from '../public/assets/images/showcase/network.png';
import pcmouseImage from '../public/assets/images/showcase/pcmouse.png';
import wiiuchatImage from '../public/assets/images/showcase/wiiuchat.png';
const showcaseImages = {
juxt: juxtImage,
network: networkImage,
pcmouse: pcmouseImage,
wiiuchat: wiiuchatImage,
};
export async function getServerSideProps(ctx) {
const locale = getLocale(ctx.locale);
return {
props: {
locale,
},
};
}
export default function Home({ locale }) {
const router = useRouter();
return (
<main>
<div id="showcase">
{locale.showcase.map((element, i) => {
return (
<ShowcaseSection
title={element.title}
caption={element.caption}
image={showcaseImages[element.image]}
isOdd={i % 2 !== 0}
key={i}
/>
);
})}
<Section className={styles.showcaseTail} contentClassName={styles.content}>
<Image
src={wiiuchatImage}
className={styles.image}
alt=""
quality={100}
sizes="(max-width: 840px) 100vw, 700px"
/>
<div className={styles.text}>
<Title>And much more!</Title>
<p className={styles.caption}>
Check out our progress page for an extensive list of our current progress and goals!
</p>
<Button
isPrimary={true}
onClick={(e) => {
e.preventDefault();
router.push('/progress');
}}
>
Check progress
</Button>
</div>
</Section>
<Section>
<Title id="faq">Frequently Asked Questions.</Title>
<p className={styles.caption}>{locale.faq.text}</p>
<Faq questionObject={locale.faq.QAs} />
</Section>
</div>
</main>
);
}

47
pages/index.module.css Normal file
View File

@@ -0,0 +1,47 @@
.caption {
font-size: 1rem;
line-height: 1.5rem;
max-width: 66%;
margin: 1.5rem 0;
}
.showcaseTail {
display: flex;
align-items: center;
background: var(--bg-shade-0);
padding: 180px 0;
}
.showcaseTail .content {
display: grid;
grid-auto-flow: row;
max-width: var(--maxwidth);
margin: auto;
gap: 40px;
}
.showcaseTail .image {
max-width: min(600px, 50%);
height: auto;
margin: auto;
}
.showcaseTail .text {
text-align: center;
}
.showcaseTail .caption {
margin: 1rem auto 2rem;
}
@media screen and (max-width: 840px) {
.showcaseTail {
padding: 90px 0;
}
.showcaseTail .image {
margin: auto;
width: 100%;
max-width: 600px;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@@ -1,6 +1,9 @@
:root {
/* 1 is the base color, <1 are darker variations and >1 are lighter */
--width: 1400px;
--maxwidth: min(var(--width), 90%);
/* 1 is the base color, <1 are darker variations and >1 are lighter */
--bg-shade-0: #131733;
--bg-shade-1: #1B1F3B;
--bg-shade-2: #23274A;
@@ -23,8 +26,6 @@
--green-shade-1: #59c9a5;
--red-shade-1: #a9375b;
--border: rgba(255, 255, 255, 0.1);
}
body {
@@ -34,12 +35,20 @@ body {
body {
width: 100%;
max-width: 100vw;
height: 100vh;
max-height: 100%;
position: relative; /* This fixes overflow-x not hiding on Safari on mobile */
overflow-x: hidden;
overflow-y: auto;
margin: 0;
color: var(--text-shade-3);
color: var(--text-shade-1);
justify-content: center;
font-family: Poppins, Arial, Helvetica, sans-serif;
}
a {
color: var(--accent-shade-1);
text-decoration: none;
font-weight: bold;
}
/* Scrollbar styling 'cause it's fancy */