This commit is contained in:
2024-01-27 15:40:32 +01:00
commit 5b02c1da03
38 changed files with 7960 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
+36
View File
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
+8
View File
@@ -0,0 +1,8 @@
{
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"bracketSameLine": false
}
+36
View File
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+12
View File
@@ -0,0 +1,12 @@
const path = require('path');
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
distDir: 'out',
sassOptions: {
includePaths: [path.join(__dirname, 'styles')],
},
};
module.exports = nextConfig;
+6551
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
{
"name": "forum",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"framer-motion": "^10.16.15",
"next": "14.0.3",
"react": "^18",
"react-country-flag": "^3.1.0",
"react-dom": "^18",
"sass": "^1.69.5"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.0.3",
"typescript": "^5"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 493 KiB

+7
View File
@@ -0,0 +1,7 @@
.main {
}
.content {
}
+20
View File
@@ -0,0 +1,20 @@
import { Metadata } from 'next';
import { openGraphMetadata } from '@/app/_helpers/shared_metadata';
import Link from 'next/link';
import styles from './page.module.scss';
export const metadata: Metadata = {
title: '',
openGraph: {
...openGraphMetadata,
title: '',
},
};
export default function Page() {
return (
<main className={styles.main}>
<div className={styles.content}></div>
</main>
);
}
+90
View File
@@ -0,0 +1,90 @@
.footer {
box-sizing: border-box;
width: 100%;
padding: 64px 32px;
background-color: var(--dark-gray);
}
.content {
margin: 0 auto;
max-width: 1400px;
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0 20px;
@media screen and (min-width: 1024px) {
grid-template-columns: repeat(10, 1fr);
}
.info,
.spacer,
.routeSection {
margin-bottom: 64px;
}
.info {
grid-column: span 3;
h4 {
display: inline-block;
font-size: 1.8rem;
font-weight: bold;
a {
text-decoration: none;
color: var(--white);
}
}
p {
color: var(--white);
margin-top: 32px;
&:first-of-type {
margin-top: 16px;
font-weight: bold;
color: var(--brand);
}
}
.div {
}
}
.routeSection {
grid-column: span 2;
h5 {
margin-bottom: 32px;
color: var(--brand);
}
a {
display: block;
margin-top: 16px;
line-height: 24px;
text-decoration: none;
color: var(--white);
&:hover {
text-decoration: underline;
}
}
}
}
.copyright {
hr {
margin-top: 0;
margin-bottom: 32px;
border-color: var(--brand);
}
p {
font-size: 0.6rem;
font-weight: normal;
color: var(--white);
}
}
}
+55
View File
@@ -0,0 +1,55 @@
'use client';
import { Metadata } from 'next';
import { openGraphMetadata } from '@/app/_helpers/shared_metadata';
import Link from 'next/link';
import styles from './footer.module.scss';
import { usePathname } from 'next/navigation';
export default function Footer() {
const pathname: string = usePathname().substring(1);
var username: string = '';
if (pathname === 'register' || pathname === 'login') {
return null;
}
return (
<footer className={styles.footer}>
<div className={styles.content}>
<div className={styles.grid}>
<div className={styles.info}>
<h4>
<Link href={`/`}>Forum name</Link>
</h4>
<p>Where curiosity thrives and knowledge grows.</p>
<p>Share on</p>
<div></div>
</div>
<div className={styles.spacer}></div>
<div className={styles.routeSection}>
<h5>Links</h5>
<Link href={'/browse'}>Browse categories</Link>
<Link href={'/create'}>Create posts</Link>
<Link href={'/account'}>My account</Link>
</div>
<div className={styles.routeSection}>
<h5>Forum name</h5>
<Link href={'/contact'}>Contact</Link>
<Link href={'/about'}>About</Link>
<Link href={'/helpcenter'}>Helpcenter</Link>
</div>
<div className={styles.routeSection}>
<h5>Policies</h5>
<Link href={'/terms'}>Terms of Service</Link>
<Link href={'/privacy'}>Privacy Policy</Link>
</div>
</div>
<div className={styles.copyright}>
<hr />
<p>&copy; {new Date().getFullYear()} forumname.nl. All rights reserved.</p>
</div>
</div>
</footer>
);
}
+128
View File
@@ -0,0 +1,128 @@
.header {
position: fixed;
z-index: 1001;
box-sizing: border-box;
width: 100%;
height: 50px;
padding: 0px 32px;
background: linear-gradient(0, #0000, var(--almost-black));
.content {
margin: auto;
max-width: 1400px;
h4 {
display: inline-block;
font-size: 1.4rem;
line-height: 50px;
font-weight: bold;
a {
text-decoration: none;
color: var(--white);
}
}
@media screen and (min-width: 600px) {
.sessionuser,
.sessionbuttons {
display: inherit !important;
}
}
.sessionuser,
.sessionbuttons {
display: none;
}
.sessionuser {
float: right;
transition: all 250ms;
margin-right: -16px;
border-radius: 0 0 8px 8px;
padding: 16px;
padding-top: 8px;
&:hover {
background-color: var(--white);
.username {
color: var(--black);
}
.dropdown {
visibility: visible;
}
}
.username {
transition: color 250ms;
line-height: 34px;
font-weight: bold;
text-align: right;
text-transform: capitalize;
color: var(--white);
&::after {
content: '\25be';
float: right;
margin-left: 4px;
}
}
.dropdown {
transition: opacity 250ms;
visibility: hidden;
a {
display: block;
margin-top: 8px;
text-align: right;
text-decoration: none;
color: var(--black);
}
}
}
.sessionbuttons {
float: right;
.register,
.login {
transition: all 250ms ease;
display: inline-block;
margin-top: 8px;
margin-left: 16px;
border-radius: 8px;
box-sizing: border-box;
width: 128px;
line-height: 34px;
font-weight: bold;
text-align: center;
text-decoration: none;
color: var(--white);
}
.register {
background-color: var(--brand);
&:hover {
background-color: color-mix(
in srgb,
var(--brand),
#000 15%
);
}
}
.login {
border: 2px solid var(--brand);
&:hover {
border: 2px solid color-mix(in srgb, var(--brand), #000 15%);
}
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
import Link from 'next/link';
import styles from './header.module.scss';
import Sessiondata from './sessiondata';
export default function Header() {
return (
<header className={styles.header}>
<div className={styles.content}>
<h4>
<Link href={`/`}>Forum name</Link>
</h4>
{/* <nav className={styles.nav}>
<p>
<Link href={`/`}></Link>
</p>
</nav> */}
<Sessiondata />
</div>
</header>
);
}
+37
View File
@@ -0,0 +1,37 @@
'use client';
import Link from 'next/link';
import styles from './header.module.scss';
import { usePathname } from 'next/navigation';
import { setSession } from '../_helpers/auth';
export default function Sessiondata() {
const pathname: string = usePathname().substring(1);
var username: string = localStorage.getItem('username') || '';
if (pathname === 'register' || pathname === 'login') {
return null;
} else if (username) {
return (
<div className={styles.sessionuser}>
<p className={styles.username}>{username}</p>
<div className={styles.dropdown}>
<Link href={'/'}>My account</Link>
<Link href={'/'} onClick={() => setSession(false)}>Logout</Link>
</div>
</div>
);
} else {
return (
<div className={styles.sessionbuttons}>
<Link href={'/register'} className={styles.register}>
Sign up
</Link>
<Link href={'/login'} className={styles.login}>
Login
</Link>
</div>
);
}
}
+157
View File
@@ -0,0 +1,157 @@
'use client';
import { Inter } from 'next/font/google';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
const inter = Inter({ subsets: ['latin'] });
export function LoginForm() {
const searchParams = useSearchParams();
var redirUrl = searchParams.get('redir');
if (localStorage.getItem('username')) {
window.location.href = '/account';
}
return (
<form action={() => {}} onSubmit={() => handleLogin(redirUrl)}>
<label htmlFor="username">Username</label>
<input
id="username"
type="text"
required
className={inter.className}
></input>
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
required
className={inter.className}
></input>
<span id="errorMessage"></span>
<button type="submit" className={inter.className}>
Login
</button>
</form>
);
}
export function RegisterForm() {
const searchParams = useSearchParams();
var redirUrl = searchParams.get('redir');
if (localStorage.getItem('username')) {
window.location.href = '/account';
}
return (
<form action={() => {}} onSubmit={() => handleRegister(redirUrl)}>
<label htmlFor="email">Email</label>
<input id="email" type="email" required></input>
<label htmlFor="username">Username</label>
<input id="username" type="text" required></input>
<label htmlFor="password">Password</label>
<input id="password" type="password" minLength={5} required></input>
<input id="consent" type="checkbox" required />
<label htmlFor="consent">
I have read and agree to Quiztimes&apos;{' '}
<Link href={'/terms'}>Terms of Service</Link>
</label>
<button type="submit" className={inter.className}>
Sign up
</button>
</form>
);
}
function handleLogin(redirUrl: string | null) {
var data = {
username: (document.getElementById('username') as HTMLInputElement)
.value,
password: (document.getElementById('password') as HTMLInputElement)
.value,
};
fetch('https://quiztimes.nl/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then((res) => {
if (res.status === 200) {
loginAndRedirect();
} else {
// display error message
(
document.getElementById('errorMessage') as HTMLSpanElement
).innerHTML = 'Incorrect username or password';
}
});
const loginAndRedirect = async () => {
// start session
await setSession(true);
// redirect if set
if (redirUrl !== null) {
window.location.href = redirUrl;
} else {
window.location.href = '/account';
}
};
}
async function handleRegister(redirUrl: string | null) {
var data = {
username: (document.getElementById('username') as HTMLInputElement)
.value,
email: (document.getElementById('email') as HTMLInputElement).value,
password: (document.getElementById('password') as HTMLInputElement)
.value,
};
fetch('https://quiztimes.nl/api/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then((res) => {
if (res.status === 200) {
loginAndRedirect();
} else {
// display error message
(
document.getElementById('errorMessage') as HTMLSpanElement
).innerHTML = 'An error occurred';
}
});
const loginAndRedirect = async () => {
// start session
await setSession(true);
// redirect if set
if (redirUrl !== null) {
window.location.href = redirUrl;
} else {
window.location.href = '/account';
}
};
}
export async function setSession(setLoggedIn: boolean) {
console.log('test');
if (setLoggedIn) {
await fetch('https://quiztimes.nl/api/jwt').then((data) => {
console.log(data);
localStorage.setItem('username', 'Vincent');
});
return true;
} else {
localStorage.clear();
window.location.href = '/';
return false;
}
}
+22
View File
@@ -0,0 +1,22 @@
export const openGraphMetadata = {
title: 'Quiztimes',
description: 'Quiztimes. Free Learning, Forever.',
url: 'https://quiztimes.nl',
siteName: 'Quiztimes',
images: [
{
url: '/og.png',
width: 1200,
height: 630,
alt: 'The quiztimes logo',
},
{
url: '/og-twitter.png',
width: 1024,
height: 512,
alt: 'The quiztimes logo',
},
],
locale: 'en_UK',
type: 'website',
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+46
View File
@@ -0,0 +1,46 @@
import '@/styles/root.scss';
import '@/styles/reset.scss';
import type { Metadata, Viewport } from 'next';
import { Inter } from 'next/font/google';
import Header from './_components/header';
import Footer from './_components/footer';
import { openGraphMetadata } from '@/app/_helpers/shared_metadata';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
metadataBase: new URL('https://Forum.nl'),
title: {
template: '%s | Forum',
default: 'Forum',
},
description: 'Asking questions, finding answers. talk about anything and everything.',
keywords: ['forum', 'questions', 'answers', 'knowledge'],
authors: [
{ name: 'Kaj van Schalkwijk', url: 'https://gitea.quiztimes.nl/kajvans' },
{ name: 'Ruben jimmink', url: 'https://gitea.quiztimes.nl/kajvans' },
],
openGraph: {
...openGraphMetadata,
},
};
export const viewport: Viewport = {
themeColor: '#646CFF'
}
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<Header />
{children}
<Footer />
</body>
</html>
);
}
+98
View File
@@ -0,0 +1,98 @@
.main {
padding-top: 0;
padding: 0 32px;
height: 100vh;
background: linear-gradient(
45deg,
var(--almost-black) 0%,
var(--dark-gray) 100%
);
.content {
position: relative;
top: 50%;
transform: translateY(-50%);
margin: auto;
border-radius: 8px;
box-sizing: border-box;
width: 500px;
padding: 32px;
background-color: var(--white);
h1 {
font-size: 2rem;
font-weight: bold;
color: var(--black);
}
label {
display: block;
margin-top: 32px;
color: var(--black);
}
input {
transition: border 250ms;
display: block;
margin-top: 8px;
border: 2px solid var(--black);
border-radius: 8px;
box-sizing: border-box;
outline: none;
width: 100%;
height: 50px;
padding: 8px;
background-color: transparent;
&:hover,
&:focus {
border: 2px solid var(--brand);
}
}
button {
transition: background-color 250ms;
display: block;
margin-top: 32px;
border: none;
border-radius: 8px;
outline: none;
width: 100%;
height: 50px;
background-color: var(--brand);
font-weight: bold;
color: var(--white);
&:hover,
&:focus {
cursor: pointer;
background-color: color-mix(in srgb, var(--brand), #000 15%);
}
}
p {
display: block;
margin-top: 32px;
font-weight: bold;
text-align: center;
color: var(--black);
a {
text-decoration: none;
color: var(--brand);
}
}
span {
display: block;
margin-top: 16px;
margin-bottom: -16px;
font-weight: bold;
text-align: center;
color: var(--red);
}
}
}
+29
View File
@@ -0,0 +1,29 @@
import { Metadata } from 'next';
import { openGraphMetadata } from '@/app/_helpers/shared_metadata';
import Link from 'next/link';
import styles from './page.module.scss';
import { LoginForm } from '@/app/_helpers/auth';
export const metadata: Metadata = {
title: 'Login',
openGraph: {
...openGraphMetadata,
title: 'Login',
url: 'https://quiztimes.nl/login',
},
};
export default function Page() {
return (
<main className={styles.main}>
<div className={styles.content}>
<h1>Welcome back!</h1>
<LoginForm />
<p>
Don&apos;t have an account?{' '}
<Link href="register">Sign up</Link>
</p>
</div>
</main>
);
}
+52
View File
@@ -0,0 +1,52 @@
@use '@/styles/buttons';
.main {
padding-top: 0;
padding: 0 32px;
height: 100vh;
background: linear-gradient(
45deg,
var(--almost-black) 0%,
var(--dark-gray) 100%
);
}
.content {
position: relative;
top: 50%;
transform: translateY(-50%);
margin: auto;
width: fit-content;
h1, h2 {
text-align: center;
color: var(--white);
}
h1 {
font-size: 2rem;
}
h2 {
margin-top: 32px;
font-size: 1rem;
font-weight: normal;
}
p, a {
margin: 0 auto;
margin-top: 16px;
text-align: center;
color: var(--white);
}
a {
&:first-of-type {
@include buttons.button;
display: block;
margin: 0 auto;
margin-top: 64px;
width: fit-content;
}
}
}
+27
View File
@@ -0,0 +1,27 @@
import { Metadata } from 'next';
import { openGraphMetadata } from '@/app/_helpers/shared_metadata';
import Link from 'next/link';
import styles from './not-found.module.scss';
export const metadata: Metadata = {
title: 'Page not found',
openGraph: {
...openGraphMetadata,
title: 'Page not found',
},
};
export default function NotFound() {
return (
<main className={styles.main}>
<div className={styles.content}>
<h1>Whoops! Looks like this page went on strike.</h1>
<h2>It&apos;s either not available or doesn&apos;t exist at all</h2>
<Link href={'/'}>Go Home</Link>
{/* <p>Or</p>
<Link href={'/helpcenter'}>Visit the Helpcenter</Link>
<Link></Link> */}
</div>
</main>
);
}
+6
View File
@@ -0,0 +1,6 @@
@use '@/styles/buttons';
.main {
padding-top: 5rem;
padding-left: 5rem;
}
+17
View File
@@ -0,0 +1,17 @@
'use client';
import Image from 'next/image';
import styles from './page.module.scss';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { useState, useEffect } from 'react';
export default function Home() {
const [selectedTheme, setSelectedTheme] = useState('black');
return (
<main className={styles.main}>
</main>
);
}
+13
View File
@@ -0,0 +1,13 @@
.main {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
font-family: 'Roboto', sans-serif;
font-size: 1.6rem;
}
.content {
}
+32
View File
@@ -0,0 +1,32 @@
import { Metadata } from 'next';
import { openGraphMetadata } from '@/app/_helpers/shared_metadata';
import Link from 'next/link';
import styles from './page.module.scss';
export const metadata: Metadata = {
title: 'Privacy Policy',
openGraph: {
...openGraphMetadata,
title: 'Privacy Policy',
},
};
export default function Page() {
return (
<main className={styles.main}>
<article className={styles.content}>
<h1>Privacy Policy</h1>
<p>Last updated: [insert date]</p>
<p>
<strong>Forum</strong> (<strong>&quot;we&quot;</strong>{' '}
or <strong>&quot;us&quot;</strong>) is committed to
protecting the information of their users.
</p>
<h2>Information We Collect</h2>
<ol>
<li></li>
</ol>
</article>
</main>
);
}
+105
View File
@@ -0,0 +1,105 @@
.main {
padding-top: 0;
padding: 0 32px;
height: 100vh;
background: linear-gradient(
45deg,
var(--almost-black) 0%,
var(--dark-gray) 100%
);
.content {
position: relative;
top: 50%;
transform: translateY(-50%);
margin: auto;
border-radius: 8px;
box-sizing: border-box;
width: 500px;
padding: 32px;
background-color: var(--white);
h1 {
font-size: 2rem;
font-weight: bold;
color: var(--black);
}
label {
display: block;
margin-top: 32px;
color: var(--black);
}
label[for='consent'] {
display: inline-block;
a {
font-weight: bold;
text-decoration: none;
color: var(--brand);
}
}
input {
transition: border 250ms;
display: block;
margin-top: 8px;
border: 2px solid var(--black);
border-radius: 8px;
box-sizing: border-box;
outline: none;
width: 100%;
height: 50px;
padding: 8px;
background-color: transparent;
&:hover,
&:focus {
border: 2px solid var(--brand);
}
}
input[type='checkbox'] {
display: inline-block;
width: auto;
height: auto;
}
button {
transition: background-color 250ms;
display: block;
margin-top: 32px;
border: none;
border-radius: 8px;
outline: none;
width: 100%;
height: 50px;
background-color: var(--brand);
font-weight: bold;
color: var(--white);
&:hover,
&:focus {
cursor: pointer;
background-color: color-mix(in srgb, var(--brand), #000 15%);
}
}
p {
display: block;
margin-top: 32px;
font-weight: bold;
text-align: center;
color: var(--black);
a {
text-decoration: none;
color: var(--brand);
}
}
}
}
+31
View File
@@ -0,0 +1,31 @@
import { Metadata } from 'next';
import { openGraphMetadata } from '@/app/_helpers/shared_metadata';
import Link from 'next/link';
import styles from './page.module.scss';
import { Inter } from 'next/font/google';
import { RegisterForm } from '../_helpers/auth';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'Create an account',
openGraph: {
...openGraphMetadata,
title: 'Create an account',
url: 'https://quiztimes.nl/register',
},
};
export default function Page() {
return (
<main className={styles.main}>
<div className={styles.content}>
<h1>Create an account</h1>
<RegisterForm />
<p>
Already have an account? <Link href="login">Log in</Link>
</p>
</div>
</main>
);
}
+15
View File
@@ -0,0 +1,15 @@
@mixin fly-in($distance: 50px) {
@keyframes fly-in {
0% {
opacity: 0;
}
66% {
opacity: 0;
transform: translateY($distance);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
}
+23
View File
@@ -0,0 +1,23 @@
@mixin button($theme: var(--brand)) {
display: inline-block;
transition: background-color 250ms ease;
border: 0;
border-radius: 8px;
padding: 16px;
background-color: $theme;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
text-decoration: none;
color: var(--white);
&:hover {
background-color: color-mix(in srgb, $theme, #000 15%);
}
&:visited {
color: var(--white);
}
}
+34
View File
@@ -0,0 +1,34 @@
@use "sass:meta";
@use "./themes/lightmode";
@use "./themes/darkmode";
$colors: (
brand: hsl(237, 100%, 70%),
white: hsl(0, 0%, 100%),
off-white: hsl(0, 0%, 96%),
gray: hsl(0, 0%, 50%),
dark-gray: hsl(0, 0%, 14%),
almost-black: hsl(0, 5%, 7%),
black: hsl(0, 0%, 0%),
red: hsl(350, 74%, 45%),
green: hsl(149, 100%, 32%),
cozy: hsl(39, 32%, 84%),
);
:root {
@each $name, $value in $colors {
--#{$name}: #{$value};
}
@media (prefers-color-scheme: light) {
@each $name, $value in meta.module-variables("lightmode") {
--#{$name}: #{$value};
}
}
@media (prefers-color-scheme: dark) {
@each $name, $value in meta.module-variables("darkmode") {
--#{$name}: #{$value};
}
}
}
+129
View File
@@ -0,0 +1,129 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html,
body,
div,
span,
applet,
object,
iframe,
dialog,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
// ol,
// ul {
// list-style: none;
// }
// blockquote,
// q {
// quotes: none;
// }
blockquote:before,
blockquote:after,
q:before,
q:after {
content: "";
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
+44
View File
@@ -0,0 +1,44 @@
@use 'colors';
body {
width: 100vw;
background-color: var(--background);
text-rendering: optimizeLegibility;
color: var(--text);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
scroll-behavior: smooth;
overscroll-behavior-y: none;
overflow-x: hidden;
}
main {
padding-top: 50px;
min-height: 100vh;
}
@media print {
@page {
size: A4; /* DIN A4 standard, Europe */
margin: 12.7mm;
}
html,
body {
width: 210mm;
/* height: 297mm; */
height: 282mm;
font-size: 11px;
background: #fff;
overflow: visible;
}
body {
padding-top: 15mm;
}
header,
footer,
aside,
nav,
form {
display: none !important;
}
}
+4
View File
@@ -0,0 +1,4 @@
$text: var(--white);
$text-secondary: var(--off-white);
$background: var(--almost-black);
$background-secondary: var(--dark-gray);
+4
View File
@@ -0,0 +1,4 @@
$text: var(--black);
$text-secondary: var(--gray);
$background: var(--white);
$background-secondary: var(--off-white);
+41
View File
@@ -0,0 +1,41 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./src/*"
]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"out/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}