unwieldy pull request
1074
package-lock.json
generated
|
@ -9,16 +9,19 @@
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"next": "13.5.4",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"next": "13.5.4"
|
"react-markdown": "^9.0.0",
|
||||||
|
"rehype-raw": "^7.0.0",
|
||||||
|
"remark-breaks": "^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5",
|
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "13.5.4"
|
"eslint-config-next": "13.5.4",
|
||||||
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
public/88x31/disqordia-approved-border.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
public/88x31/dotart.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
public/88x31/firefox4.gif
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
public/88x31/ipg.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
public/88x31/ivorybutton.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
public/88x31/kaboom3.gif
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
public/88x31/kkdiagt.png
Normal file
After Width: | Height: | Size: 491 B |
BIN
public/88x31/pjfrix2023.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
public/88x31/spacy_webbutton.png
Normal file
After Width: | Height: | Size: 588 B |
BIN
public/88x31/sun_88x31_dual_border.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
public/88x31/tla.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
public/fonts/Renogare/Renogare.otf
Normal file
BIN
public/fonts/Renogare/Renogare.woff
Normal file
BIN
public/fonts/Renogare/Renogare.woff2
Normal file
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
Before Width: | Height: | Size: 1.3 KiB |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
|
Before Width: | Height: | Size: 629 B |
0
src/app/api/status/index.tsx
Normal file
Before Width: | Height: | Size: 25 KiB |
|
@ -1,107 +0,0 @@
|
||||||
:root {
|
|
||||||
--max-width: 1100px;
|
|
||||||
--border-radius: 12px;
|
|
||||||
--font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
|
|
||||||
'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
|
|
||||||
'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
|
|
||||||
|
|
||||||
--foreground-rgb: 0, 0, 0;
|
|
||||||
--background-start-rgb: 214, 219, 220;
|
|
||||||
--background-end-rgb: 255, 255, 255;
|
|
||||||
|
|
||||||
--primary-glow: conic-gradient(
|
|
||||||
from 180deg at 50% 50%,
|
|
||||||
#16abff33 0deg,
|
|
||||||
#0885ff33 55deg,
|
|
||||||
#54d6ff33 120deg,
|
|
||||||
#0071ff33 160deg,
|
|
||||||
transparent 360deg
|
|
||||||
);
|
|
||||||
--secondary-glow: radial-gradient(
|
|
||||||
rgba(255, 255, 255, 1),
|
|
||||||
rgba(255, 255, 255, 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
--tile-start-rgb: 239, 245, 249;
|
|
||||||
--tile-end-rgb: 228, 232, 233;
|
|
||||||
--tile-border: conic-gradient(
|
|
||||||
#00000080,
|
|
||||||
#00000040,
|
|
||||||
#00000030,
|
|
||||||
#00000020,
|
|
||||||
#00000010,
|
|
||||||
#00000010,
|
|
||||||
#00000080
|
|
||||||
);
|
|
||||||
|
|
||||||
--callout-rgb: 238, 240, 241;
|
|
||||||
--callout-border-rgb: 172, 175, 176;
|
|
||||||
--card-rgb: 180, 185, 188;
|
|
||||||
--card-border-rgb: 131, 134, 135;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--foreground-rgb: 255, 255, 255;
|
|
||||||
--background-start-rgb: 0, 0, 0;
|
|
||||||
--background-end-rgb: 0, 0, 0;
|
|
||||||
|
|
||||||
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
|
|
||||||
--secondary-glow: linear-gradient(
|
|
||||||
to bottom right,
|
|
||||||
rgba(1, 65, 255, 0),
|
|
||||||
rgba(1, 65, 255, 0),
|
|
||||||
rgba(1, 65, 255, 0.3)
|
|
||||||
);
|
|
||||||
|
|
||||||
--tile-start-rgb: 2, 13, 46;
|
|
||||||
--tile-end-rgb: 2, 5, 19;
|
|
||||||
--tile-border: conic-gradient(
|
|
||||||
#ffffff80,
|
|
||||||
#ffffff40,
|
|
||||||
#ffffff30,
|
|
||||||
#ffffff20,
|
|
||||||
#ffffff10,
|
|
||||||
#ffffff10,
|
|
||||||
#ffffff80
|
|
||||||
);
|
|
||||||
|
|
||||||
--callout-rgb: 20, 20, 20;
|
|
||||||
--callout-border-rgb: 108, 108, 108;
|
|
||||||
--card-rgb: 100, 100, 100;
|
|
||||||
--card-border-rgb: 200, 200, 200;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
max-width: 100vw;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: rgb(var(--foreground-rgb));
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
transparent,
|
|
||||||
rgb(var(--background-end-rgb))
|
|
||||||
)
|
|
||||||
rgb(var(--background-start-rgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
html {
|
|
||||||
color-scheme: dark;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +1,26 @@
|
||||||
import './globals.css'
|
import "@/styles/globals.css";
|
||||||
import type { Metadata } from 'next'
|
import fonts from "@/utility/fonts";
|
||||||
import { Inter } from 'next/font/google'
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
const inter = Inter({ subsets: ['latin'] })
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Create Next App',
|
title: "MeowcaTheoRange",
|
||||||
description: 'Generated by create next app',
|
description: "hehe :3c",
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en" className={fonts}>
|
||||||
<body className={inter.className}>{children}</body>
|
<head>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>{children}</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,229 +0,0 @@
|
||||||
.main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 6rem;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
display: inherit;
|
|
||||||
justify-content: inherit;
|
|
||||||
align-items: inherit;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
max-width: var(--max-width);
|
|
||||||
width: 100%;
|
|
||||||
z-index: 2;
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.description a {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description p {
|
|
||||||
position: relative;
|
|
||||||
margin: 0;
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: rgba(var(--callout-rgb), 0.5);
|
|
||||||
border: 1px solid rgba(var(--callout-border-rgb), 0.3);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
font-weight: 700;
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(4, minmax(25%, auto));
|
|
||||||
max-width: 100%;
|
|
||||||
width: var(--max-width);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 1rem 1.2rem;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
background: rgba(var(--card-rgb), 0);
|
|
||||||
border: 1px solid rgba(var(--card-border-rgb), 0);
|
|
||||||
transition: background 200ms, border 200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card span {
|
|
||||||
display: inline-block;
|
|
||||||
transition: transform 200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card h2 {
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card p {
|
|
||||||
margin: 0;
|
|
||||||
opacity: 0.6;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
line-height: 1.5;
|
|
||||||
max-width: 30ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
padding: 4rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center::before {
|
|
||||||
background: var(--secondary-glow);
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 480px;
|
|
||||||
height: 360px;
|
|
||||||
margin-left: -400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center::after {
|
|
||||||
background: var(--primary-glow);
|
|
||||||
width: 240px;
|
|
||||||
height: 180px;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center::before,
|
|
||||||
.center::after {
|
|
||||||
content: '';
|
|
||||||
left: 50%;
|
|
||||||
position: absolute;
|
|
||||||
filter: blur(45px);
|
|
||||||
transform: translateZ(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
/* Enable hover only on non-touch devices */
|
|
||||||
@media (hover: hover) and (pointer: fine) {
|
|
||||||
.card:hover {
|
|
||||||
background: rgba(var(--card-rgb), 0.1);
|
|
||||||
border: 1px solid rgba(var(--card-border-rgb), 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover span {
|
|
||||||
transform: translateX(4px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion) {
|
|
||||||
.card:hover span {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile */
|
|
||||||
@media (max-width: 700px) {
|
|
||||||
.content {
|
|
||||||
padding: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
margin-bottom: 120px;
|
|
||||||
max-width: 320px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 1rem 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card h2 {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
padding: 8rem 0 6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center::before {
|
|
||||||
transform: none;
|
|
||||||
height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description a {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description p,
|
|
||||||
.description div {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description p {
|
|
||||||
align-items: center;
|
|
||||||
inset: 0 0 auto;
|
|
||||||
padding: 2rem 1rem 1.4rem;
|
|
||||||
border-radius: 0;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba(var(--background-start-rgb), 1),
|
|
||||||
rgba(var(--callout-rgb), 0.5)
|
|
||||||
);
|
|
||||||
background-clip: padding-box;
|
|
||||||
backdrop-filter: blur(24px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.description div {
|
|
||||||
align-items: flex-end;
|
|
||||||
pointer-events: none;
|
|
||||||
inset: auto 0 0;
|
|
||||||
padding: 2rem;
|
|
||||||
height: 200px;
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
transparent 0%,
|
|
||||||
rgb(var(--background-end-rgb)) 40%
|
|
||||||
);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tablet and Smaller Desktop */
|
|
||||||
@media (min-width: 701px) and (max-width: 1120px) {
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: repeat(2, 50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.vercelLogo {
|
|
||||||
filter: invert(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rotate {
|
|
||||||
from {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
}
|
|
855
src/app/page.tsx
|
@ -1,95 +1,776 @@
|
||||||
import Image from 'next/image'
|
/* eslint-disable react/jsx-no-comment-textnodes */
|
||||||
import styles from './page.module.css'
|
/* eslint-disable react/no-unescaped-entities */
|
||||||
|
"use client";
|
||||||
|
import ColourChip from "@/components/ColourChip/ColourChip";
|
||||||
|
import Eighty from "@/components/Eighty/Eighty";
|
||||||
|
import Floaty from "@/components/Floaty/Floaty";
|
||||||
|
import Page from "@/components/Page/Page";
|
||||||
|
import ProjectList, { Project } from "@/components/ProjectList/ProjectList";
|
||||||
|
import ScrollBackInd from "@/components/ScrollBackInd/ScrollBackInd";
|
||||||
|
import SpeedDial from "@/components/SpeedDial/SpeedDial";
|
||||||
|
import Time from "@/components/Time/Time";
|
||||||
|
import LastFM from "@/components/net/LastFM/LastFM";
|
||||||
|
import { Color3 } from "@/utility/color";
|
||||||
|
import { Space_Grotesk } from "next/font/google";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
const space_grotesk = Space_Grotesk({
|
||||||
|
subsets: ["latin"],
|
||||||
|
variable: "--font-Space-Grotesk",
|
||||||
|
});
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
const [page, setPage] = useState(0);
|
||||||
|
const body = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// TIME
|
||||||
|
const dateObj = new Date();
|
||||||
|
const [time, setTime] = useState("00:00:00 PM");
|
||||||
|
const [date, setDate] = useState("0/0/0000");
|
||||||
|
let animFrame = useRef(0);
|
||||||
|
|
||||||
|
// hot reloading memory saver
|
||||||
|
useEffect(() => {
|
||||||
|
window.cancelAnimationFrame(animFrame.current);
|
||||||
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
function setTheTime() {
|
||||||
|
dateObj.setTime(Date.now());
|
||||||
|
setTime(
|
||||||
|
dateObj.toLocaleTimeString("en-US", {
|
||||||
|
minute: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
timeZone: "America/Chicago",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setDate(
|
||||||
|
dateObj.toLocaleDateString("en-US", {
|
||||||
|
weekday: "long",
|
||||||
|
day: "2-digit",
|
||||||
|
month: "long",
|
||||||
|
timeZone: "America/Chicago",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
animFrame.current = window.requestAnimationFrame(setTheTime);
|
||||||
|
}
|
||||||
|
animFrame.current = window.requestAnimationFrame(setTheTime);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// LAST FM
|
||||||
|
let [player, setPlayer] = useState<{ [key: string]: any } | null | undefined>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const FMGate = useRef(true);
|
||||||
|
useEffect(() => {
|
||||||
|
async function LastFMGet() {
|
||||||
|
FMGate.current = false;
|
||||||
|
const api_key = "8f9b0255cc55a19f82d37c22600aff1a";
|
||||||
|
const LAST_FM_URL = `https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=MeowcaTheoRange&api_key=${api_key}&format=json&extended=1&limit=1`;
|
||||||
|
const res = await fetch(LAST_FM_URL);
|
||||||
|
setPlayer(await res.json());
|
||||||
|
setTimeout(() => LastFMGet(), 20000);
|
||||||
|
}
|
||||||
|
if (FMGate.current) LastFMGet();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Gallery
|
||||||
|
let [images, setImages] = useState<Project[]>([
|
||||||
|
{
|
||||||
|
name: "See More",
|
||||||
|
description: "blog.abtmtr.link",
|
||||||
|
url: "https://blog.abtmtr.link/",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const ImageGate = useRef(true);
|
||||||
|
useEffect(() => {
|
||||||
|
async function ImageGet() {
|
||||||
|
ImageGate.current = false;
|
||||||
|
const IMAGE_URL = `https://img.abtmtr.link/api/collections/mtr/posts`;
|
||||||
|
const res = await fetch(IMAGE_URL);
|
||||||
|
const gallery = (await res.json()).data;
|
||||||
|
setImages([
|
||||||
|
...gallery.posts?.map((imagePost: any) => ({
|
||||||
|
name: imagePost.title,
|
||||||
|
description: imagePost.body.replace(
|
||||||
|
/^!\[.*?\]\((.*?)\).*(?:\n\nCharacters:.*?\n\n)(.*)\n\n.*$/gs,
|
||||||
|
"$2"
|
||||||
|
),
|
||||||
|
image: imagePost.images[0] ?? null,
|
||||||
|
url: "https://img.abtmtr.link/" + imagePost.slug,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: "See More",
|
||||||
|
description: "img.abtmtr.link",
|
||||||
|
url: "https://img.abtmtr.link/",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (ImageGate.current) ImageGet();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Gallery
|
||||||
|
let [blog, setBlog] = useState<Project[]>([
|
||||||
|
{
|
||||||
|
name: "See More",
|
||||||
|
description: "blog.abtmtr.link",
|
||||||
|
url: "https://blog.abtmtr.link/mtr/",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const BlogGate = useRef(true);
|
||||||
|
useEffect(() => {
|
||||||
|
async function BlogGet() {
|
||||||
|
BlogGate.current = false;
|
||||||
|
const BLOG_URL = `https://blog.abtmtr.link/api/collections/mtr/posts`;
|
||||||
|
const res = await fetch(BLOG_URL);
|
||||||
|
const blogss = (await res.json()).data;
|
||||||
|
setBlog([
|
||||||
|
...blogss.posts?.map((blogPost: any) => ({
|
||||||
|
name: blogPost.title,
|
||||||
|
description: blogPost.body.replace(/#\w*/g, ""),
|
||||||
|
url: "https://blog.abtmtr.link/mtr/" + blogPost.slug,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: "See More",
|
||||||
|
description: "blog.abtmtr.link",
|
||||||
|
url: "https://blog.abtmtr.link/mtr/",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (BlogGate.current) BlogGet();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Gallery
|
||||||
|
let [repos, setRepos] = useState<Project[]>([
|
||||||
|
{
|
||||||
|
name: "See More",
|
||||||
|
description: "github.com",
|
||||||
|
url: "https://github.com/MeowcaTheoRange",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const GithubGate = useRef(true);
|
||||||
|
useEffect(() => {
|
||||||
|
async function GithubGet() {
|
||||||
|
GithubGate.current = false;
|
||||||
|
const GITHUB_URL = `https://api.github.com/users/meowcatheorange/starred`;
|
||||||
|
const res = await fetch(GITHUB_URL);
|
||||||
|
const reposs = (await res.json())?.filter(
|
||||||
|
(x: any) => x.owner.login == "MeowcaTheoRange"
|
||||||
|
);
|
||||||
|
console.log(reposs);
|
||||||
|
setRepos([
|
||||||
|
...reposs?.map((repository: any) => ({
|
||||||
|
name: repository.name,
|
||||||
|
description: repository.description ?? "No description",
|
||||||
|
url: repository.html_url,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: "See More",
|
||||||
|
description: "github.com",
|
||||||
|
url: "https://github.com/MeowcaTheoRange",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (GithubGate.current) GithubGet();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className={styles.main}>
|
<div
|
||||||
<div className={styles.description}>
|
className="body"
|
||||||
|
ref={body}
|
||||||
|
onScroll={(e) => {
|
||||||
|
const target = e.target as HTMLDivElement;
|
||||||
|
const curPage = Math.round(target.scrollTop / target.clientHeight);
|
||||||
|
if (curPage != page) setPage(curPage);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ScrollBackInd
|
||||||
|
hide={page <= 0}
|
||||||
|
scroll={body.current}
|
||||||
|
player={player}
|
||||||
|
time={time}
|
||||||
|
/>
|
||||||
|
<Page
|
||||||
|
scroll={body.current}
|
||||||
|
color={Color3.fromHex("ff4000")}
|
||||||
|
bg={`linear-gradient(0deg, #200800ee, #200800ee)${
|
||||||
|
player?.recenttracks.track[0].image[2]["#text"] != null
|
||||||
|
? `, no-repeat center/cover url("${player.recenttracks.track[0].image[2]["#text"]}")`
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
id="top"
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
ABTMTR
|
||||||
|
<wbr />
|
||||||
|
.LINK
|
||||||
|
</h1>
|
||||||
|
<Floaty>
|
||||||
|
<LastFM player={player} />
|
||||||
|
</Floaty>
|
||||||
|
<Time time={time} date={date} />
|
||||||
|
<Floaty start>
|
||||||
|
<p className="hv">Scroll down for more!</p>
|
||||||
|
</Floaty>
|
||||||
|
</Page>
|
||||||
|
<Page scroll={body.current} color={Color3.fromHex("FFFFFF")} id="hi">
|
||||||
|
<h1>Welcome 👋</h1>
|
||||||
<p>
|
<p>
|
||||||
Get started by editing
|
I'm <b>MeowcaTheoRange</b> <small>(miau-kuh-thee~oh-ray~nj)</small>.
|
||||||
<code className={styles.code}>src/app/page.tsx</code>
|
<br />
|
||||||
|
I'm a web developer, Fediverse enthusiast, and compulsory Minnesotan.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<p>
|
||||||
<a
|
I'm also known as <strong>Iszac</strong> or <strong>Theo</strong> as
|
||||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
well.
|
||||||
target="_blank"
|
</p>
|
||||||
rel="noopener noreferrer"
|
<p className="chip">he/they/it</p>
|
||||||
>
|
<p className="chip">Male</p>
|
||||||
By{' '}
|
<p className="chip">Minor</p>
|
||||||
<Image
|
<p className="chip">Autistic</p>
|
||||||
src="/vercel.svg"
|
<p>I run this domain and all of the services on it.</p>
|
||||||
alt="Vercel Logo"
|
<p>
|
||||||
className={styles.vercelLogo}
|
My favourite hobbies are <strong>programming</strong>,{" "}
|
||||||
width={100}
|
<strong>drawing</strong>,{" "}
|
||||||
height={24}
|
<strong>occasionally making small bits of music</strong>,{" "}
|
||||||
priority
|
<strong>obsessing over fonts</strong>, and{" "}
|
||||||
/>
|
<strong>being pedantic</strong>.
|
||||||
</a>
|
</p>
|
||||||
</div>
|
<p>
|
||||||
</div>
|
You may see parts of all of these aspects within this website. Please
|
||||||
|
tread carefully.
|
||||||
<div className={styles.center}>
|
</p>
|
||||||
<Image
|
<p>
|
||||||
className={styles.logo}
|
Some other important things I think you should know about me are...
|
||||||
src="/next.svg"
|
</p>
|
||||||
alt="Next.js Logo"
|
<ul>
|
||||||
width={180}
|
<li>Please be patient with me.</li>
|
||||||
height={37}
|
<li>
|
||||||
priority
|
Please be understanding! Ask me for clarification if required.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
I don't really like small talk - keep if brief if you want to check
|
||||||
|
up on me, please.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
I'm not one to pick sides at first, usually. Being an "all or
|
||||||
|
nothing" kind of person isn't my thing, and if you don't like that,
|
||||||
|
feel free to tell me why your side is good.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
I like getting tangled up in drama, but I'm not a spiteful person -
|
||||||
|
I'm usually only in it for the correlations.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
You may see me hyperfixate on random stuff, like{" "}
|
||||||
|
<a href="#sc_fonts" className="inline">
|
||||||
|
certain fonts
|
||||||
|
</a>{" "}
|
||||||
|
or public transit.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>I believe in...</p>
|
||||||
|
<ul>
|
||||||
|
<li>Self-hosting important or personal infrastructure</li>
|
||||||
|
<li>Free and open-source material</li>
|
||||||
|
<li>Privacy as a basic human right</li>
|
||||||
|
<li>Trans rights & gay rights</li>
|
||||||
|
<li>
|
||||||
|
Autistic superiority <span className="hv">/joke</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>Check me out on:</p>
|
||||||
|
<SpeedDial
|
||||||
|
services={[
|
||||||
|
{
|
||||||
|
name: "moth.zone",
|
||||||
|
url: "https://moth.zone/meowcatheorange",
|
||||||
|
purpose: "The Fediverse",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "discord.com",
|
||||||
|
url: "http://discord.trollcall.xyz/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "github.com",
|
||||||
|
url: "http://github.com/MeowcaTheoRange",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ko-fi.com",
|
||||||
|
url: "https://ko-fi.com/meowcatheorange",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mspfa.com",
|
||||||
|
url: "https://mspfa.com/user/?u=109014333296332953331",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta.trollcall.xyz",
|
||||||
|
url: "https://beta.trollcall.xyz/clan/meowcatheorange",
|
||||||
|
golden: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Page>
|
||||||
|
<Page scroll={body.current} color={Color3.fromHex("00c0ff")}>
|
||||||
|
<h1>What's on this domain?</h1>
|
||||||
|
<p>
|
||||||
|
Here's a quick list of all of the web services on this domain right
|
||||||
|
now.
|
||||||
|
</p>
|
||||||
|
<ProjectList
|
||||||
|
projects={[
|
||||||
|
{
|
||||||
|
name: "abtmtr.link",
|
||||||
|
description: "You are here!",
|
||||||
|
url: "https://abtmtr.link/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "abs.abtmtr.link",
|
||||||
|
description:
|
||||||
|
"An instance of ABS, a link normalizer for personal use.",
|
||||||
|
url: "https://abs.abtmtr.link/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "blog.abtmtr.link",
|
||||||
|
description: "An instance of WriteFreely, used for my blog(s).",
|
||||||
|
url: "https://blog.abtmtr.link/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cdn.abtmtr.link",
|
||||||
|
description:
|
||||||
|
"A CDN full of images I use, usually for my blog or gallery.",
|
||||||
|
url: "https://cdn.abtmtr.link/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "img.abtmtr.link",
|
||||||
|
description:
|
||||||
|
"An instance of WriteFreely, used as an art gallery.",
|
||||||
|
url: "https://img.abtmtr.link/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local.abtmtr.link",
|
||||||
|
description: "An instance of Akkoma, running on a home server.",
|
||||||
|
url: "https://local.abtmtr.link/",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
markdown
|
||||||
|
/>
|
||||||
|
<h2>Planned services</h2>
|
||||||
|
<p>What I would like to put on this domain sometime in the future.</p>
|
||||||
|
<ProjectList
|
||||||
|
projects={[
|
||||||
|
{
|
||||||
|
name: "Drawpile",
|
||||||
|
description:
|
||||||
|
"Running Drawpile on my domain, for use by friends and such.",
|
||||||
|
url: "https://pile.abtmtr.link/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Minecraft",
|
||||||
|
description:
|
||||||
|
"Replace the craft.trollcall.xyz server with something a little nicer.",
|
||||||
|
url: "https://craft.abtmtr.link/",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
markdown
|
||||||
|
/>
|
||||||
|
<h2>
|
||||||
|
L10n <small>(localization)</small>
|
||||||
|
</h2>
|
||||||
|
<p>What services I'd like to put on my server computer at home.</p>
|
||||||
|
<ProjectList
|
||||||
|
projects={[
|
||||||
|
{
|
||||||
|
name: "blog.abtmtr.link",
|
||||||
|
description:
|
||||||
|
"It's running on Oracle Cloud right now. That's not good.",
|
||||||
|
url: "https://blog.abtmtr.link/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "img.abtmtr.link",
|
||||||
|
description: "Ditto.",
|
||||||
|
url: "https://img.abtmtr.link/",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
markdown
|
||||||
|
/>
|
||||||
|
</Page>
|
||||||
|
<Page
|
||||||
|
scroll={body.current}
|
||||||
|
color={Color3.fromHex("FF80C0")}
|
||||||
|
id="currents"
|
||||||
|
preview
|
||||||
|
>
|
||||||
|
<h1>Current Obsessions</h1>
|
||||||
|
<p>
|
||||||
|
I'm into a lot of stuff. As of this site's publication, you'll
|
||||||
|
probably see me indulging in:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://social.translunar.academy/@winter"
|
||||||
|
className="inline"
|
||||||
|
>
|
||||||
|
@winter@translunar.academy
|
||||||
|
</a>
|
||||||
|
's fic{" "}
|
||||||
|
<a href="https://translunar.academy/fic/Aetherglow" target="_blank">
|
||||||
|
Ætherglow
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>Discussion of autism</li>
|
||||||
|
<li>Discussion and usage of the Fediverse</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://homestuck.com/" target="_blank">
|
||||||
|
Homestuck
|
||||||
|
</a>{" "}
|
||||||
|
and related properties
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Adventures on{" "}
|
||||||
|
<a href="https://mspfa.com/" target="_blank">
|
||||||
|
MSPFA
|
||||||
|
</a>
|
||||||
|
, sometimes
|
||||||
|
</li>
|
||||||
|
<li>Using React or Next.js</li>
|
||||||
|
<li>Creating alternatives to both because I'm just that cool</li>
|
||||||
|
<li>Anti-JS practices</li>
|
||||||
|
<li>Art and drawing</li>
|
||||||
|
<li>Using Blender</li>
|
||||||
|
<li>The MetroTransit bus system</li>
|
||||||
|
</ul>
|
||||||
|
<p>and possibly much more that I can't even remember.</p>
|
||||||
|
<p>
|
||||||
|
Want to know more? Below is my display of some of these things in
|
||||||
|
sections. Enjoy!
|
||||||
|
</p>
|
||||||
|
</Page>
|
||||||
|
<Page
|
||||||
|
scroll={body.current}
|
||||||
|
color={Color3.fromHex("80FF00")}
|
||||||
|
preview
|
||||||
|
id="sc_programming"
|
||||||
|
>
|
||||||
|
<h1>Programming</h1>
|
||||||
|
<p>I like using what some call "programming languages".</p>
|
||||||
|
<p>
|
||||||
|
What do I call them? None of your business. All you have to know is
|
||||||
|
that I use <strong>TypeScript</strong>, <strong>React</strong>, and{" "}
|
||||||
|
<strong>Haxe</strong>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I also know <strong>JavaScript</strong> and some <strong>Bash</strong>{" "}
|
||||||
|
in case of emergency.
|
||||||
|
</p>
|
||||||
|
<ProjectList projects={repos} />
|
||||||
|
</Page>
|
||||||
|
<Page
|
||||||
|
scroll={body.current}
|
||||||
|
color={Color3.fromHex("80ffff")}
|
||||||
|
preview
|
||||||
|
floaty
|
||||||
|
id="sc_fonts"
|
||||||
|
>
|
||||||
|
<h1>Fonts</h1>
|
||||||
|
<p>I also like UI and UI design. This includes fonts, quite a bit.</p>
|
||||||
|
<p>
|
||||||
|
Currently, I'm liking the look of <b>Renogare</b>, <b>Lexend Deca</b>,
|
||||||
|
and <b>Space Grotesk</b>. This may change.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I first used <strong>Space Grotesk</strong> for general-purpose
|
||||||
|
applications. This was on my old portfolio site and it's on TrollCall
|
||||||
|
right now. I think it just fits TrollCall; it's quirky, geometric, a
|
||||||
|
bit alien - it's right at home with the intended vibe of the site.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
On this website, I'm using <strong>Renogare</strong> as a header/title
|
||||||
|
font, and <strong>Lexend Deca</strong> for everything else.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I found <strong>Renogare</strong> while playing{" "}
|
||||||
|
<strong>Celeste</strong> - yaknow, the game where you climb a huge
|
||||||
|
mountain for a few days?
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Renogare</strong> is a bold, geometric, display-optimized
|
||||||
|
font. It's <i>super</i> cool and I am in love with how it looks.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Lexend Deca</strong> is a very strong second to{" "}
|
||||||
|
<strong>Renogare</strong>, primarily used when I can't use{" "}
|
||||||
|
<strong>Renogare</strong>, shouldn't use <strong>Renogare</strong>, or
|
||||||
|
am using <strong>Renogare</strong> but need something that{" "}
|
||||||
|
<i>isn't</i> <strong>Renogare</strong>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Lexend Deca</strong> is similar-looking to{" "}
|
||||||
|
<strong>Renogare</strong>, with a few major differences.{" "}
|
||||||
|
<strong>Lexend Deca</strong> loses the geometry, instead opting for a
|
||||||
|
beamed I, flat-top A, M, N, et al. t loses its curve, instead going
|
||||||
|
straight down.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
It also has different weights and proper OTF support - plus a bonus
|
||||||
|
series of different letter spacing choices under the{" "}
|
||||||
|
<strong>Lexend</strong> name. <strong>Deca</strong> is the tightest,
|
||||||
|
which is why I chose it.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
All in all, <strong>Lexend Deca</strong> is a good general-purpose
|
||||||
|
font, while still leaving room for <strong>Renogare</strong> to be a
|
||||||
|
great display font.
|
||||||
|
</p>
|
||||||
|
<Floaty>
|
||||||
|
<div>
|
||||||
|
<h1
|
||||||
|
style={{
|
||||||
|
fontFamily: "var(--font-Renogare)",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
AaIiLlMm
|
||||||
|
</h1>
|
||||||
|
<p
|
||||||
|
className="hv"
|
||||||
|
style={{
|
||||||
|
fontFamily: "var(--font-Renogare)",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Renogare
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1
|
||||||
|
style={{
|
||||||
|
fontFamily: "var(--font-Lexend-Deca)",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
AaIiLlMm
|
||||||
|
</h1>
|
||||||
|
<p
|
||||||
|
className="hv"
|
||||||
|
style={{
|
||||||
|
fontFamily: "var(--font-Lexend-Deca)",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Lexend Deca
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className={space_grotesk.variable}>
|
||||||
|
<h1
|
||||||
|
style={{
|
||||||
|
fontFamily: "var(--font-Space-Grotesk)",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
AaIiLlMm
|
||||||
|
</h1>
|
||||||
|
<p
|
||||||
|
className="hv"
|
||||||
|
style={{
|
||||||
|
fontFamily: "var(--font-Space-Grotesk)",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Space Grotesk
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Floaty>
|
||||||
|
</Page>
|
||||||
|
<Page scroll={body.current} color={Color3.fromHex("00c0ff")} id="sc_art">
|
||||||
|
<h1>Artistry</h1>
|
||||||
|
<p>
|
||||||
|
Artistry usually includes writing and drawing. These are the purposes
|
||||||
|
for{" "}
|
||||||
|
<a href="https://blog.abtmtr.link" className="inline">
|
||||||
|
my blog
|
||||||
|
</a>{" "}
|
||||||
|
and{" "}
|
||||||
|
<a href="https://img.abtmtr.link" className="inline">
|
||||||
|
my public gallery
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
<ProjectList projects={images} markdown />
|
||||||
|
<ProjectList projects={blog} markdown double />
|
||||||
|
</Page>
|
||||||
|
<Page
|
||||||
|
scroll={body.current}
|
||||||
|
color={Color3.fromHex("FFFFFF")}
|
||||||
|
id="branding"
|
||||||
|
>
|
||||||
|
<h1>Branding</h1>
|
||||||
|
<p>
|
||||||
|
I don't really have strict branding guidelines, but I do have a few
|
||||||
|
important rules if you would like to refer to me in a professional or
|
||||||
|
formal context.
|
||||||
|
</p>
|
||||||
|
<h2>Name</h2>
|
||||||
|
<p>
|
||||||
|
If you would like to refer to me online, you'll usually be able to use
|
||||||
|
my username <b>MeowcaTheoRange</b>. If that's too long, you can always
|
||||||
|
shorten it to <strong>MTR</strong>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please make sure to keep the styling. It's <b>MeowcaTheoRange</b>, not{" "}
|
||||||
|
<strong>MeowcatHeOrange</strong>, <strong>MeowcaTheOrange</strong>,
|
||||||
|
nor <strong>Meowca Theo Range</strong>.
|
||||||
|
</p>
|
||||||
|
<p className="hv">
|
||||||
|
The styling <strong>meowcatheorange</strong> is OK if necessary.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you would like to refer to me in a more professional context, my
|
||||||
|
name <strong>Iszac</strong> or the moniker <strong>Theo Range</strong>{" "}
|
||||||
|
will work just as well.
|
||||||
|
</p>
|
||||||
|
<p className="hv">
|
||||||
|
If you are a local organization and would like to refer to me by my
|
||||||
|
legal name, please{" "}
|
||||||
|
<a href="#top" className="inline">
|
||||||
|
contact me
|
||||||
|
</a>{" "}
|
||||||
|
and we can probably figure something out.
|
||||||
|
<br />
|
||||||
|
Local means <strong>within Minnesota</strong>, by the way.
|
||||||
|
</p>
|
||||||
|
<h2>Colours</h2>
|
||||||
|
<p>
|
||||||
|
If you would like to use colours to refer to me, whether that be the
|
||||||
|
primary color on a card or the color of my name, I recommend you use
|
||||||
|
these colours:
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className={styles.grid}>
|
<ColourChip colour={new Color3(0, 0.75, 1)}>
|
||||||
<a
|
<b>Iszac Blue</b>
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
<br />
|
||||||
className={styles.card}
|
<small>Primary</small>
|
||||||
target="_blank"
|
</ColourChip>
|
||||||
rel="noopener noreferrer"
|
<ColourChip colour={new Color3(1, 0.25, 0)}>
|
||||||
>
|
<b>Rocco Orange</b>
|
||||||
<h2>
|
<br />
|
||||||
Docs <span>-></span>
|
<small>Primary Negative</small>
|
||||||
</h2>
|
</ColourChip>
|
||||||
<p>Find in-depth information about Next.js features and API.</p>
|
<p style={{ color: "#00BFFF" }}>
|
||||||
</a>
|
<b>Iszac Blue</b> is literally <strong>Rocco Orange</strong> but
|
||||||
|
inverted. This colour is named after <strong>Iszac</strong>, an OC of
|
||||||
<a
|
mine that I came up with, for the setting of Ætherglow.
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
<br />
|
||||||
className={styles.card}
|
<small>
|
||||||
target="_blank"
|
...and then I shortly named myself after said character...
|
||||||
rel="noopener noreferrer"
|
</small>
|
||||||
>
|
<br />
|
||||||
<h2>
|
If you were to use any of these colours to represnt me as a{" "}
|
||||||
Learn <span>-></span>
|
<strong>person</strong>, use this one.
|
||||||
</h2>
|
</p>
|
||||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
<p style={{ color: "#FF4000" }}>
|
||||||
</a>
|
<b>Rocco Orange</b> is my favourite colour orange, but now more red.
|
||||||
|
The name comes from my character <strong>Rocco</strong>, whose hair is
|
||||||
<a
|
this colour - though this colour has been applied to more characters
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
like <strong>BLEND-1020</strong>.<br />
|
||||||
className={styles.card}
|
If you were to use any of these colours to represnt me as an{" "}
|
||||||
target="_blank"
|
<strong>entity</strong>, use this one whenever possible.
|
||||||
rel="noopener noreferrer"
|
</p>
|
||||||
>
|
<br />
|
||||||
<h2>
|
<ColourChip colour={new Color3(0.5, 0, 1)}>
|
||||||
Templates <span>-></span>
|
<b>Grape Soda</b>
|
||||||
</h2>
|
<br />
|
||||||
<p>Explore the Next.js 13 playground.</p>
|
<small>Secondary</small>
|
||||||
</a>
|
</ColourChip>
|
||||||
|
<ColourChip colour={new Color3(0.5, 1, 0)}>
|
||||||
<a
|
<b>Avalonian Waste</b>
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
<br />
|
||||||
className={styles.card}
|
<small>Secondary Negative</small>
|
||||||
target="_blank"
|
</ColourChip>
|
||||||
rel="noopener noreferrer"
|
<p style={{ color: "#8000FF" }}>
|
||||||
>
|
<b>Grape Soda</b> represents my love for grape soda.
|
||||||
<h2>
|
<br />
|
||||||
Deploy <span>-></span>
|
My favourite is Fanta Grape. :]
|
||||||
</h2>
|
</p>
|
||||||
<p>
|
<p style={{ color: "#80FF00" }}>
|
||||||
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
<b>Avalonian Waste</b> is also literally <strong>Grape Soda</strong>{" "}
|
||||||
</p>
|
but inverted. This represents some{" "}
|
||||||
</a>
|
<a
|
||||||
</div>
|
href="https://mlr.fandom.com/wiki/Ayden"
|
||||||
</main>
|
target="_blank"
|
||||||
)
|
className="inline"
|
||||||
|
>
|
||||||
|
old lore
|
||||||
|
</a>{" "}
|
||||||
|
that I'm still screwing with.
|
||||||
|
</p>
|
||||||
|
</Page>
|
||||||
|
<Page scroll={body.current} color={Color3.fromHex("000000")}></Page>
|
||||||
|
<Page
|
||||||
|
scroll={body.current}
|
||||||
|
color={Color3.fromHex("ffffff")}
|
||||||
|
footer
|
||||||
|
bg="center/50px 50px repeating-linear-gradient(45deg, #80808010, #80808010 25%, transparent 25%, transparent 50%, #80808010 50%, #80808010 75%, transparent 75%, transparent 100%)"
|
||||||
|
id="footer"
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
ABTMTR
|
||||||
|
<wbr />
|
||||||
|
.LINK
|
||||||
|
</h1>
|
||||||
|
<Eighty img="/88x31/dotart.png" alt="blocked by dotart" />
|
||||||
|
<Eighty
|
||||||
|
url="https://www.mozilla.org/en-US/firefox/new/"
|
||||||
|
img="/88x31/firefox4.gif"
|
||||||
|
alt="tested on Firefox"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://homestuck.com/"
|
||||||
|
img="/88x31/sun_88x31_dual_border.png"
|
||||||
|
alt="HOMESTUCK"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://dimden.dev/"
|
||||||
|
img="https://dimden.dev/services/images/88x31.gif"
|
||||||
|
alt="DIMDEN"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://park-city.club/~frix/"
|
||||||
|
img="/88x31/pjfrix2023.png"
|
||||||
|
alt="pjfrix"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://invoxiplaygames.uk/"
|
||||||
|
img="/88x31/ipg.png"
|
||||||
|
alt="Invoxi PlayGames"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://ioletsgo.gay/"
|
||||||
|
img="/88x31/ivorybutton.gif"
|
||||||
|
alt="ioletsgo.gay"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://spacy.neocities.org/"
|
||||||
|
img="/88x31/spacy_webbutton.png"
|
||||||
|
alt="Spacy =)"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://disqordia.space/"
|
||||||
|
img="/88x31/disqordia-approved-border.png"
|
||||||
|
alt="Disqordia Approved"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://translunar.academy/"
|
||||||
|
img="/88x31/tla.png"
|
||||||
|
alt="TRANSLUNAR ACADEMY"
|
||||||
|
/>
|
||||||
|
<Eighty
|
||||||
|
url="https://moth.zone/meowcatheorange"
|
||||||
|
img="/88x31/kkdiagt.png"
|
||||||
|
alt="KARKATDYINGIN AGLUETRAP.COM R.I.P"
|
||||||
|
/>
|
||||||
|
<p className="hv">© MeowcaTheoRange 2023</p>
|
||||||
|
</Page>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
9
src/components/ColourChip/ColourChip.module.css
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.ColourChip {
|
||||||
|
background-color: var(--mainColour);
|
||||||
|
display: inline-block;
|
||||||
|
width: 9em;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
padding: 16px;
|
||||||
|
vertical-align: top;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
28
src/components/ColourChip/ColourChip.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { Color3 } from "@/utility/color";
|
||||||
|
import styles from "./ColourChip.module.css";
|
||||||
|
|
||||||
|
export default function ColourChip({
|
||||||
|
colour,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
colour: Color3;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const calcTextColor = +(Math.max(...colour.toRGB()) <= 127);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--mainColour": "#" + colour.toHex(),
|
||||||
|
color:
|
||||||
|
"#" +
|
||||||
|
new Color3(calcTextColor, calcTextColor, calcTextColor).toHex(),
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
className={styles.ColourChip}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<p>{"#" + colour.toHex()}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
11
src/components/Eighty/Eighty.module.css
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.Eighty {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 5px 6px;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Eighty img {
|
||||||
|
display: inline-block;
|
||||||
|
width: 88px;
|
||||||
|
height: 31px;
|
||||||
|
}
|
22
src/components/Eighty/Eighty.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import Link from "next/link";
|
||||||
|
import styles from "./Eighty.module.css";
|
||||||
|
|
||||||
|
export default function Eighty({
|
||||||
|
url,
|
||||||
|
img,
|
||||||
|
alt,
|
||||||
|
}: {
|
||||||
|
url?: string;
|
||||||
|
img: string;
|
||||||
|
alt?: string;
|
||||||
|
}) {
|
||||||
|
return url ? (
|
||||||
|
<Link className={styles.Eighty} href={url} target="_blank">
|
||||||
|
<img src={img} alt={alt} title={alt} />
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span className={styles.Eighty}>
|
||||||
|
<img src={img} alt={alt} title={alt} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
55
src/components/Floaty/Floaty.module.css
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
.Floaty {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: calc(50vw - 64px);
|
||||||
|
min-width: 373px;
|
||||||
|
/* height: calc(50vh - 64px); */
|
||||||
|
position: absolute !important;
|
||||||
|
right: 64px;
|
||||||
|
bottom: 64px;
|
||||||
|
text-align: end;
|
||||||
|
/* background-color: #0001;
|
||||||
|
box-shadow: 0 0 8px currentColor;
|
||||||
|
padding: 16px; */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.FloatySticky {
|
||||||
|
position: sticky;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Floaty:dir(rtl) {
|
||||||
|
right: unset;
|
||||||
|
left: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.FloatyTop {
|
||||||
|
bottom: unset;
|
||||||
|
top: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.FloatyStart {
|
||||||
|
right: unset;
|
||||||
|
left: 64px;
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.FloatyStart:dir(rtl) {
|
||||||
|
left: unset;
|
||||||
|
right: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 875px) {
|
||||||
|
.Floaty {
|
||||||
|
position: static !important;
|
||||||
|
text-align: start;
|
||||||
|
display: block;
|
||||||
|
margin: 2em 0;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
.FloatyKeep {
|
||||||
|
position: absolute !important;
|
||||||
|
text-align: unset;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
27
src/components/Floaty/Floaty.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import styles from "./Floaty.module.css";
|
||||||
|
|
||||||
|
export default function Floaty({
|
||||||
|
children,
|
||||||
|
top = false,
|
||||||
|
start = false,
|
||||||
|
keepFloat = false,
|
||||||
|
sticky = false,
|
||||||
|
}: {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
top?: boolean;
|
||||||
|
start?: boolean;
|
||||||
|
keepFloat?: boolean;
|
||||||
|
sticky?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${styles.Floaty} ${top ? styles.FloatyTop : ""} ${
|
||||||
|
start ? styles.FloatyStart : ""
|
||||||
|
} ${keepFloat ? styles.FloatyKeep : ""} ${
|
||||||
|
sticky ? styles.FloatySticky : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
0
src/components/Nav/Nav.module.css
Normal file
22
src/components/Nav/Nav.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Color3 } from "@/utility/color";
|
||||||
|
import styles from "./Nav.module.css";
|
||||||
|
|
||||||
|
export default function Nav({
|
||||||
|
children,
|
||||||
|
color,
|
||||||
|
}: {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
color?: Color3;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.Nav}
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#" + color?.darken(90).toHex(),
|
||||||
|
color: "#" + color?.lighten(80).toHex(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
59
src/components/Page/Page.module.css
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
.Page {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
/* overflow-y: auto; */
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 64px;
|
||||||
|
scroll-snap-align: start;
|
||||||
|
scroll-snap-stop: always;
|
||||||
|
position: relative;
|
||||||
|
background-attachment: fixed;
|
||||||
|
z-index: 1;
|
||||||
|
scroll-margin-bottom: calc(64px + (3rem + 1.5rem));
|
||||||
|
}
|
||||||
|
|
||||||
|
.Page > * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Page .PageBgElements {
|
||||||
|
z-index: 2;
|
||||||
|
/* opacity: 0.5; */
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 200%;
|
||||||
|
/* transform: translateY(-50%); */
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
speak: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PagePreview {
|
||||||
|
min-height: calc(100vh - (64px + (3rem + 1.5rem)));
|
||||||
|
}
|
||||||
|
|
||||||
|
.PageFooter {
|
||||||
|
min-height: 50vh;
|
||||||
|
scroll-snap-align: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PageSpace {
|
||||||
|
padding-right: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 1024px) {
|
||||||
|
.Page {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 875px) {
|
||||||
|
.PageSpace {
|
||||||
|
padding-right: 64px;
|
||||||
|
}
|
||||||
|
}
|
57
src/components/Page/Page.tsx
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { Color3 } from "@/utility/color";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import styles from "./Page.module.css";
|
||||||
|
|
||||||
|
export default function Page({
|
||||||
|
children,
|
||||||
|
color,
|
||||||
|
bg,
|
||||||
|
scroll,
|
||||||
|
preview = false,
|
||||||
|
footer = false,
|
||||||
|
floaty = false,
|
||||||
|
id,
|
||||||
|
}: {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
color?: Color3;
|
||||||
|
bg?: string;
|
||||||
|
scroll: HTMLElement | null;
|
||||||
|
preview?: boolean;
|
||||||
|
footer?: boolean;
|
||||||
|
floaty?: boolean;
|
||||||
|
id?: string;
|
||||||
|
}) {
|
||||||
|
const pageobj = useRef<HTMLDivElement>(null);
|
||||||
|
const pageScrollAnim = useRef(0);
|
||||||
|
useEffect(() => {
|
||||||
|
if (scroll == null) return;
|
||||||
|
const handler = () => {
|
||||||
|
if (pageobj.current == null) return;
|
||||||
|
pageobj.current.style.backgroundPositionY =
|
||||||
|
-(pageobj.current.offsetTop - scroll.scrollTop) / 2 + "px";
|
||||||
|
pageScrollAnim.current = requestAnimationFrame(handler);
|
||||||
|
};
|
||||||
|
pageScrollAnim.current = requestAnimationFrame(handler);
|
||||||
|
// return cancelAnimationFrame(pageScrollAnim.current);
|
||||||
|
}, [scroll]);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id={id}
|
||||||
|
ref={pageobj}
|
||||||
|
className={`${styles.Page} ${preview ? styles.PagePreview : ""} ${
|
||||||
|
footer ? styles.PageFooter : ""
|
||||||
|
} ${floaty ? styles.PageSpace : ""} block`}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--backgroundColor": "#" + color?.darken(75).toHex(),
|
||||||
|
"--color": "#" + color?.toHex(),
|
||||||
|
background: bg,
|
||||||
|
backgroundColor: "#" + color?.darken(90).toHex(),
|
||||||
|
color: "var(--color)",
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
113
src/components/ProjectList/ProjectList.module.css
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
.ProjectList {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-auto-columns: 300px;
|
||||||
|
gap: 16px;
|
||||||
|
width: calc(100% + 128px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: -64px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectListDoubleWide {
|
||||||
|
grid-auto-columns: 616px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectList .ProjectListProject {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px solid var(--color);
|
||||||
|
background-color: var(--backgroundColor);
|
||||||
|
color: var(--color);
|
||||||
|
box-shadow: 0 0 0 var(--color);
|
||||||
|
font-size: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: box-shadow 0.125s, transform 0.125s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectListDoubleWide .ProjectListProject {
|
||||||
|
aspect-ratio: 2 / 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectList .ProjectListProject:hover {
|
||||||
|
box-shadow: 0 8px 0 var(--color);
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectList .ProjectListProject:active {
|
||||||
|
box-shadow: 0 4px 0 var(--color);
|
||||||
|
transform: translateY(-4px);
|
||||||
|
font-weight: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectListProject .ProjectListProjectImage {
|
||||||
|
position: absolute;
|
||||||
|
left: -8px;
|
||||||
|
top: -8px;
|
||||||
|
width: calc(100% + 16px);
|
||||||
|
height: calc(100% + 16px);
|
||||||
|
object-fit: cover;
|
||||||
|
filter: blur(4px) brightness(30%);
|
||||||
|
transition: top 0.125s, filter 0.125s;
|
||||||
|
/* opacity: 0.75; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectListProject:hover .ProjectListProjectImage {
|
||||||
|
top: 0px;
|
||||||
|
filter: blur(8px) brightness(30%);
|
||||||
|
/* opacity: 0.75; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectListProject .ProjectListProjectDescription {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectListProjectDescription .ProjectListProjectDescriptionTitle {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Renogare";
|
||||||
|
font-size: 1.5em;
|
||||||
|
max-lines: 2;
|
||||||
|
line-clamp: 2;
|
||||||
|
line-height: 1.25em;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectListProjectDescription .ProjectListProjectDescriptionDescription {
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 0.25em;
|
||||||
|
font-family: "Lexend Deca";
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 750px) {
|
||||||
|
.ProjectList {
|
||||||
|
grid-auto-columns: calc(50% - 8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectListDoubleWide {
|
||||||
|
grid-auto-columns: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProjectList .ProjectListProject {
|
||||||
|
}
|
||||||
|
}
|
71
src/components/ProjectList/ProjectList.tsx
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import rehypeRaw from "rehype-raw";
|
||||||
|
import remarkBreaks from "remark-breaks";
|
||||||
|
import styles from "./ProjectList.module.css";
|
||||||
|
|
||||||
|
export type Project = {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
description: string;
|
||||||
|
image?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ProjectList({
|
||||||
|
projects,
|
||||||
|
markdown = false,
|
||||||
|
double = false,
|
||||||
|
}: {
|
||||||
|
projects: Project[];
|
||||||
|
markdown?: boolean;
|
||||||
|
double?: boolean;
|
||||||
|
}) {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${styles.ProjectList} ${
|
||||||
|
double ? styles.ProjectListDoubleWide : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{projects.map((project, i) => (
|
||||||
|
<a
|
||||||
|
key={i}
|
||||||
|
className={`${styles.ProjectListProject} notInline`}
|
||||||
|
href={project.url}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{project.image ? (
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
src={project.image}
|
||||||
|
className={styles.ProjectListProjectImage}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
<div className={styles.ProjectListProjectDescription}>
|
||||||
|
<p className={styles.ProjectListProjectDescriptionTitle}>
|
||||||
|
{project.name}
|
||||||
|
</p>
|
||||||
|
<p className={styles.ProjectListProjectDescriptionDescription}>
|
||||||
|
{markdown ? (
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkBreaks]}
|
||||||
|
rehypePlugins={[rehypeRaw]}
|
||||||
|
allowedElements={["br"]}
|
||||||
|
unwrapDisallowed
|
||||||
|
>
|
||||||
|
{project.description.replace(/\n/gi, " \n")}
|
||||||
|
</ReactMarkdown>
|
||||||
|
) : (
|
||||||
|
project.description
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
65
src/components/ScrollBackInd/ScrollBackInd.module.css
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
.ScrollBackInd {
|
||||||
|
position: fixed;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: 16px;
|
||||||
|
left: 50vw;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
bottom: 64px;
|
||||||
|
/* width: 64px; */
|
||||||
|
height: 64px;
|
||||||
|
z-index: 9;
|
||||||
|
background-color: #0008;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
transition: opacity 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ScrollBackInd span:first-child {
|
||||||
|
display: inline-block;
|
||||||
|
width: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 24px;
|
||||||
|
transition: text-shadow 0.25s;
|
||||||
|
font-family: "Material Symbols Outlined";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ScrollBackInd:hover span:first-child {
|
||||||
|
text-shadow: 0 0 8px #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ScrollBackInd span {
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 0 8px;
|
||||||
|
font-family: var(--font-Lexend-Deca);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ScrollBackInd img {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: conic-gradient(
|
||||||
|
from -90deg at 50% 50%,
|
||||||
|
transparent 0%,
|
||||||
|
transparent 25%,
|
||||||
|
currentColor 30%,
|
||||||
|
currentColor 45%,
|
||||||
|
transparent 50%,
|
||||||
|
transparent 75%,
|
||||||
|
currentColor 80%,
|
||||||
|
currentColor 95%,
|
||||||
|
transparent 100%
|
||||||
|
)
|
||||||
|
border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ScrollBackIndHidden {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
36
src/components/ScrollBackInd/ScrollBackInd.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
"use client";
|
||||||
|
import styles from "./ScrollBackInd.module.css";
|
||||||
|
|
||||||
|
export default function ScrollBackInd<T>({
|
||||||
|
hide = false,
|
||||||
|
scroll,
|
||||||
|
player,
|
||||||
|
time,
|
||||||
|
}: {
|
||||||
|
hide?: boolean;
|
||||||
|
scroll: HTMLElement | null;
|
||||||
|
player: { [key: string]: any } | null | undefined;
|
||||||
|
time: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`${styles.ScrollBackInd} ${
|
||||||
|
hide ? styles.ScrollBackIndHidden : ""
|
||||||
|
}`}
|
||||||
|
onClick={() => scroll?.scrollTo({ top: 0, left: 0, behavior: "smooth" })}
|
||||||
|
>
|
||||||
|
<span className={`icon`}>arrow_upward</span>
|
||||||
|
<span>{time}</span>
|
||||||
|
{player &&
|
||||||
|
player?.recenttracks.track[0]["@attr"]?.nowplaying === "true" ? (
|
||||||
|
<img
|
||||||
|
className={styles.ScrollBackIndAlbumArt}
|
||||||
|
src={player?.recenttracks.track[0].image[1]["#text"]}
|
||||||
|
alt=""
|
||||||
|
></img>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
27
src/components/Sides/Sides.module.css
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
.Sides {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto max-content;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Sides .SidesStart {
|
||||||
|
text-align: start;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Sides .SidesEnd {
|
||||||
|
text-align: end;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 750px) {
|
||||||
|
.Sides {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Sides .SidesEnd {
|
||||||
|
text-align: start;
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
}
|
16
src/components/Sides/Sides.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import styles from "./Sides.module.css";
|
||||||
|
|
||||||
|
export default function Sides({
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
}: {
|
||||||
|
start?: React.ReactNode;
|
||||||
|
end?: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={`${styles.Sides} block`}>
|
||||||
|
<div className={`${styles.SidesStart} block`}>{start}</div>
|
||||||
|
<div className={`${styles.SidesEnd} block`}>{end}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
18
src/components/SpeedDial/SpeedDial.module.css
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
.SpeedDial {
|
||||||
|
/* display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0 0.5em; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.SpeedDialService {
|
||||||
|
/* display: inline-block; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.SpeedDialServiceGolden {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SpeedDialServicePurpose {
|
||||||
|
margin: 0;
|
||||||
|
}
|
34
src/components/SpeedDial/SpeedDial.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import styles from "./SpeedDial.module.css";
|
||||||
|
|
||||||
|
export default function SpeedDial({
|
||||||
|
services,
|
||||||
|
}: {
|
||||||
|
services: {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
golden?: boolean;
|
||||||
|
purpose?: string;
|
||||||
|
}[];
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<ul className={styles.SpeedDial}>
|
||||||
|
{services.map((service, iter) => (
|
||||||
|
<li
|
||||||
|
className={`${styles.SpeedDialService} ${
|
||||||
|
service.golden ? styles.SpeedDialServiceGolden : ""
|
||||||
|
}`}
|
||||||
|
key={iter}
|
||||||
|
>
|
||||||
|
{service.purpose ? (
|
||||||
|
<p className={styles.SpeedDialServicePurpose}>{service.purpose}</p>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
<a href={service.url} className="special">
|
||||||
|
{service.name}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
2
src/components/Time/Time.module.css
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.Time {
|
||||||
|
}
|
11
src/components/Time/Time.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import styles from "./Time.module.css";
|
||||||
|
|
||||||
|
export default function Time({ time, date }: { time: string; date: string }) {
|
||||||
|
return (
|
||||||
|
<div className={styles.Time}>
|
||||||
|
<h2>{time}</h2>
|
||||||
|
<h3>{date}</h3>
|
||||||
|
<h4 className="hv">Central Time (Minnesota)</h4>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
55
src/components/net/LastFM/LastFM.module.css
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
.LastFM {
|
||||||
|
display: inline-grid;
|
||||||
|
grid-template-columns: auto 96px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
/* background: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
var(--backgroundColor) 0%,
|
||||||
|
var(--backgroundColor) 100%
|
||||||
|
)
|
||||||
|
padding-box,
|
||||||
|
conic-gradient(
|
||||||
|
from -90deg at 50% 50%,
|
||||||
|
transparent 0%,
|
||||||
|
transparent 35%,
|
||||||
|
currentColor 40%,
|
||||||
|
currentColor 45%,
|
||||||
|
transparent 50%,
|
||||||
|
transparent 85%,
|
||||||
|
currentColor 90%,
|
||||||
|
currentColor 95%,
|
||||||
|
transparent 100%
|
||||||
|
)
|
||||||
|
border-box; */
|
||||||
|
/* box-shadow: 0 0 4px currentColor; */
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* padding: 16px; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.LastFM .LastFMAlbumArt {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.LastFM .LastFMMetadata {
|
||||||
|
}
|
||||||
|
|
||||||
|
.LastFM .LastFMMetadata .LastFMMetadataTitle {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.LastFMError {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 875px) {
|
||||||
|
.LastFM {
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-template-columns: 96px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.LastFM .LastFMAlbumArt {
|
||||||
|
order: -1;
|
||||||
|
}
|
||||||
|
}
|
38
src/components/net/LastFM/LastFM.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
"use client";
|
||||||
|
import styles from "./LastFM.module.css";
|
||||||
|
|
||||||
|
export default function LastFM({
|
||||||
|
player,
|
||||||
|
}: {
|
||||||
|
player: { [key: string]: any } | null | undefined;
|
||||||
|
}) {
|
||||||
|
return player != null ? (
|
||||||
|
<>
|
||||||
|
{player?.recenttracks.track[0]["@attr"]?.nowplaying === "true" ? (
|
||||||
|
<p>Currently listening to</p>
|
||||||
|
) : (
|
||||||
|
<p className={styles.LastFMError}>Last listened to</p>
|
||||||
|
)}
|
||||||
|
<div className={styles.LastFM}>
|
||||||
|
<div className={styles.LastFMMetadata}>
|
||||||
|
<p className={styles.LastFMMetadataTitle}>
|
||||||
|
{player?.recenttracks.track[0].name}
|
||||||
|
</p>
|
||||||
|
<p className={styles.LastFMMetadataArtist}>
|
||||||
|
{player?.recenttracks.track[0].artist.name} -{" "}
|
||||||
|
{player?.recenttracks.track[0].album["#text"]}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
className={styles.LastFMAlbumArt}
|
||||||
|
src={player?.recenttracks.track[0].image[2]["#text"]}
|
||||||
|
alt=""
|
||||||
|
width="96"
|
||||||
|
height="96"
|
||||||
|
></img>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className={styles.LastFMError}>Hold on...</p>
|
||||||
|
);
|
||||||
|
}
|
178
src/styles/globals.css
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
:root {
|
||||||
|
font-family: var(--font-Lexend-Deca);
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
overflow-y: scroll;
|
||||||
|
scroll-snap-type: y mandatory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Markup */
|
||||||
|
|
||||||
|
.block > h1,
|
||||||
|
.block > h2,
|
||||||
|
.block > h3,
|
||||||
|
.block > h4 {
|
||||||
|
max-width: 85%;
|
||||||
|
}
|
||||||
|
.block > p,
|
||||||
|
.block > ul,
|
||||||
|
.block > ol {
|
||||||
|
max-width: 65%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-Renogare);
|
||||||
|
font-size: 3rem;
|
||||||
|
margin: 0 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-wrap: balance;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-family: var(--font-Renogare);
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-wrap: balance;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-family: var(--font-Renogare);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
text-wrap: balance;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-family: var(--font-Renogare);
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
text-wrap: balance;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0.5rem 1em;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-size: 1.25em;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
color: currentColor;
|
||||||
|
transform: skew(0);
|
||||||
|
transform-origin: bottom left;
|
||||||
|
transition: all 0.125s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.special::after {
|
||||||
|
content: " >>>";
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
width: 1ch;
|
||||||
|
overflow: hidden;
|
||||||
|
transform: scaleX(0);
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
transition: all 0.125s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration-color: transparent;
|
||||||
|
transform: skew(-15deg, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
a.special:hover {
|
||||||
|
letter-spacing: 0.25ch;
|
||||||
|
margin-right: 2ch;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration-color: transparent;
|
||||||
|
transform: skew(-15deg, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
a.special:hover::after {
|
||||||
|
right: -2ch;
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.special:active {
|
||||||
|
transform: skew(-30deg, 0);
|
||||||
|
letter-spacing: 0.5ch;
|
||||||
|
margin-right: 5ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.special:active::after {
|
||||||
|
width: 4ch;
|
||||||
|
right: -5ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-family: "Material Symbols Outlined";
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stinky styling */
|
||||||
|
|
||||||
|
.hv {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--backgroundColor);
|
||||||
|
color: var(--color);
|
||||||
|
padding: 0 1em;
|
||||||
|
margin-left: 0.25em;
|
||||||
|
margin-right: 0.25em;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 1024px) {
|
||||||
|
:root {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
94
src/utility/color.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
export type ColorTypes = [number, number, number];
|
||||||
|
|
||||||
|
const clamp = (n: number, mi: number, ma: number) =>
|
||||||
|
Math.max(mi, Math.min(n, ma));
|
||||||
|
|
||||||
|
export class Color3 {
|
||||||
|
R: number;
|
||||||
|
G: number;
|
||||||
|
B: number;
|
||||||
|
constructor(red: number, green: number, blue: number) {
|
||||||
|
this.R = red;
|
||||||
|
this.G = green;
|
||||||
|
this.B = blue;
|
||||||
|
}
|
||||||
|
static clone(color3: Color3) {
|
||||||
|
return new Color3(color3.R, color3.G, color3.B);
|
||||||
|
}
|
||||||
|
static fromRGB(red: number, green: number, blue: number) {
|
||||||
|
return new Color3(red / 255, green / 255, blue / 255);
|
||||||
|
}
|
||||||
|
static fromHex(hex: string) {
|
||||||
|
// @ts-ignore
|
||||||
|
const hexSplit: [number, number, number] = (
|
||||||
|
hex.match(new RegExp(`[0-9a-f]{1,${hex.length / 3}}`, "gi")) ?? [
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
]
|
||||||
|
).map((x) => parseInt(x, 16) / 255);
|
||||||
|
return new Color3(...hexSplit);
|
||||||
|
}
|
||||||
|
static fromInt(int: number) {
|
||||||
|
return new Color3(
|
||||||
|
(int & 0xff0000) >> 16,
|
||||||
|
(int & 0x00ff00) >> 8,
|
||||||
|
int & 0x0000ff
|
||||||
|
);
|
||||||
|
}
|
||||||
|
static assumeColor(
|
||||||
|
value: [number, number, number] | string | number,
|
||||||
|
rgb?: boolean
|
||||||
|
) {
|
||||||
|
if (Color3.isColor(value)) {
|
||||||
|
if (Array.isArray(value))
|
||||||
|
return rgb ? Color3.fromRGB(...value) : new Color3(...value);
|
||||||
|
else if (typeof value === "string") return Color3.fromHex(value);
|
||||||
|
else if (typeof value === "number") return Color3.fromInt(value);
|
||||||
|
}
|
||||||
|
throw new Error("Not a valid color type");
|
||||||
|
}
|
||||||
|
static isColor(
|
||||||
|
value: [number, number, number] | string | number,
|
||||||
|
rgb?: boolean
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
(Array.isArray(value) &&
|
||||||
|
value.length === 3 &&
|
||||||
|
value.every((x) => typeof x === "number" && !isNaN(x))) ||
|
||||||
|
(typeof value === "string" && !isNaN(parseInt(value, 16))) ||
|
||||||
|
(typeof value === "number" && !isNaN(value))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toHex() {
|
||||||
|
return this.toInt().toString(16).padStart(6, "0");
|
||||||
|
}
|
||||||
|
toInt() {
|
||||||
|
return (
|
||||||
|
(Math.round(this.R * 255) << 16) +
|
||||||
|
(Math.round(this.G * 255) << 8) +
|
||||||
|
Math.round(this.B * 255)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
toRGB(): [number, number, number] {
|
||||||
|
return [this.R * 255, this.G * 255, this.B * 255];
|
||||||
|
}
|
||||||
|
multiply(mult: number) {
|
||||||
|
return new Color3(this.R * mult, this.G * mult, this.B * mult);
|
||||||
|
}
|
||||||
|
lighten(mult: number) {
|
||||||
|
return new Color3(
|
||||||
|
clamp(this.R + (mult / 100) * (1 - this.R), 0, 1),
|
||||||
|
clamp(this.G + (mult / 100) * (1 - this.G), 0, 1),
|
||||||
|
clamp(this.B + (mult / 100) * (1 - this.B), 0, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
darken(mult: number) {
|
||||||
|
return new Color3(
|
||||||
|
clamp(this.R - (mult / 100) * this.R, 0, 1),
|
||||||
|
clamp(this.G - (mult / 100) * this.G, 0, 1),
|
||||||
|
clamp(this.B - (mult / 100) * this.B, 0, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
19
src/utility/fonts.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { Lexend_Deca } from "next/font/google";
|
||||||
|
import Renogare from "next/font/local";
|
||||||
|
|
||||||
|
const lexend_deca = Lexend_Deca({
|
||||||
|
subsets: ["latin"],
|
||||||
|
variable: "--font-Lexend-Deca",
|
||||||
|
});
|
||||||
|
const lexend_deca_backup = Lexend_Deca({
|
||||||
|
// some dumb error
|
||||||
|
subsets: ["latin"],
|
||||||
|
variable: "--font-Lexend-Deca",
|
||||||
|
});
|
||||||
|
const renogare = Renogare({
|
||||||
|
src: "./../../public/fonts/Renogare/Renogare.woff2",
|
||||||
|
variable: "--font-Renogare",
|
||||||
|
});
|
||||||
|
|
||||||
|
const fonts = `${lexend_deca.variable} ${lexend_deca_backup.variable} ${renogare.variable}`;
|
||||||
|
export default fonts;
|