unwieldy pull request

This commit is contained in:
MeowcaTheoRange 2023-11-11 10:42:59 -06:00
parent 69bd5ff760
commit cf4e26f4c9
50 changed files with 2919 additions and 452 deletions

1074
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -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"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
public/88x31/dotart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
public/88x31/firefox4.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
public/88x31/ipg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
public/88x31/kaboom3.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
public/88x31/kkdiagt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

BIN
public/88x31/pjfrix2023.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

BIN
public/88x31/tla.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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

View file

@ -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

View file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View file

@ -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;
}
}

View file

@ -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>
) );
} }

View file

@ -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);
}
}

View file

@ -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() {
return ( const [page, setPage] = useState(0);
<main className={styles.main}> const body = useRef<HTMLDivElement>(null);
<div className={styles.description}>
<p>
Get started by editing&nbsp;
<code className={styles.code}>src/app/page.tsx</code>
</p>
<div>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
By{' '}
<Image
src="/vercel.svg"
alt="Vercel Logo"
className={styles.vercelLogo}
width={100}
height={24}
priority
/>
</a>
</div>
</div>
<div className={styles.center}> // TIME
<Image const dateObj = new Date();
className={styles.logo} const [time, setTime] = useState("00:00:00 PM");
src="/next.svg" const [date, setDate] = useState("0/0/0000");
alt="Next.js Logo" let animFrame = useRef(0);
width={180}
height={37}
priority
/>
</div>
<div className={styles.grid}> // hot reloading memory saver
<a useEffect(() => {
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app" window.cancelAnimationFrame(animFrame.current);
className={styles.card} }, []);
target="_blank" useEffect(() => {
rel="noopener noreferrer" function setTheTime() {
> dateObj.setTime(Date.now());
<h2> setTime(
Docs <span>-&gt;</span> dateObj.toLocaleTimeString("en-US", {
</h2> minute: "2-digit",
<p>Find in-depth information about Next.js features and API.</p> hour: "2-digit",
</a> timeZone: "America/Chicago",
})
<a );
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app" setDate(
className={styles.card} dateObj.toLocaleDateString("en-US", {
target="_blank" weekday: "long",
rel="noopener noreferrer" day: "2-digit",
> month: "long",
<h2> timeZone: "America/Chicago",
Learn <span>-&gt;</span> })
</h2> );
<p>Learn about Next.js in an interactive course with&nbsp;quizzes!</p> animFrame.current = window.requestAnimationFrame(setTheTime);
</a> }
animFrame.current = window.requestAnimationFrame(setTheTime);
<a }, []);
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card} // LAST FM
target="_blank" let [player, setPlayer] = useState<{ [key: string]: any } | null | undefined>(
rel="noopener noreferrer" null
> );
<h2> const FMGate = useRef(true);
Templates <span>-&gt;</span> useEffect(() => {
</h2> async function LastFMGet() {
<p>Explore the Next.js 13 playground.</p> FMGate.current = false;
</a> 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`;
<a const res = await fetch(LAST_FM_URL);
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app" setPlayer(await res.json());
className={styles.card} setTimeout(() => LastFMGet(), 20000);
target="_blank" }
rel="noopener noreferrer" if (FMGate.current) LastFMGet();
> }, []);
<h2>
Deploy <span>-&gt;</span> // Gallery
</h2> let [images, setImages] = useState<Project[]>([
<p> {
Instantly deploy your Next.js site to a shareable URL with Vercel. name: "See More",
</p> description: "blog.abtmtr.link",
</a> url: "https://blog.abtmtr.link/",
</div> },
</main> ]);
) 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 (
<div
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>
I'm <b>MeowcaTheoRange</b> <small>(miau-kuh-thee~oh-ray~nj)</small>.
<br />
I'm a web developer, Fediverse enthusiast, and compulsory Minnesotan.
</p>
<p>
I'm also known as <strong>Iszac</strong> or <strong>Theo</strong> as
well.
</p>
<p className="chip">he/they/it</p>
<p className="chip">Male</p>
<p className="chip">Minor</p>
<p className="chip">Autistic</p>
<p>I run this domain and all of the services on it.</p>
<p>
My favourite hobbies are <strong>programming</strong>,{" "}
<strong>drawing</strong>,{" "}
<strong>occasionally making small bits of music</strong>,{" "}
<strong>obsessing over fonts</strong>, and{" "}
<strong>being pedantic</strong>.
</p>
<p>
You may see parts of all of these aspects within this website. Please
tread carefully.
</p>
<p>
Some other important things I think you should know about me are...
</p>
<ul>
<li>Please be patient with me.</li>
<li>
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,
},
]}
/>
</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>
<ColourChip colour={new Color3(0, 0.75, 1)}>
<b>Iszac Blue</b>
<br />
<small>Primary</small>
</ColourChip>
<ColourChip colour={new Color3(1, 0.25, 0)}>
<b>Rocco Orange</b>
<br />
<small>Primary Negative</small>
</ColourChip>
<p style={{ color: "#00BFFF" }}>
<b>Iszac Blue</b> is literally <strong>Rocco Orange</strong> but
inverted. This colour is named after <strong>Iszac</strong>, an OC of
mine that I came up with, for the setting of Ætherglow.
<br />
<small>
...and then I shortly named myself after said character...
</small>
<br />
If you were to use any of these colours to represnt me as a{" "}
<strong>person</strong>, use this one.
</p>
<p style={{ color: "#FF4000" }}>
<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
this colour - though this colour has been applied to more characters
like <strong>BLEND-1020</strong>.<br />
If you were to use any of these colours to represnt me as an{" "}
<strong>entity</strong>, use this one whenever possible.
</p>
<br />
<ColourChip colour={new Color3(0.5, 0, 1)}>
<b>Grape Soda</b>
<br />
<small>Secondary</small>
</ColourChip>
<ColourChip colour={new Color3(0.5, 1, 0)}>
<b>Avalonian Waste</b>
<br />
<small>Secondary Negative</small>
</ColourChip>
<p style={{ color: "#8000FF" }}>
<b>Grape Soda</b> represents my love for grape soda.
<br />
My favourite is Fanta Grape. :]
</p>
<p style={{ color: "#80FF00" }}>
<b>Avalonian Waste</b> is also literally <strong>Grape Soda</strong>{" "}
but inverted. This represents some{" "}
<a
href="https://mlr.fandom.com/wiki/Ayden"
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>
);
} }

View 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;
}

View 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>
);
}

View file

@ -0,0 +1,11 @@
.Eighty {
display: inline-block;
margin: 5px 6px;
transform-origin: center;
}
.Eighty img {
display: inline-block;
width: 88px;
height: 31px;
}

View 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>
);
}

View 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;
}
}

View 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>
);
}

View file

View 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>
);
}

View 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;
}
}

View 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>
);
}

View 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 {
}
}

View 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, "&nbsp;\n")}
</ReactMarkdown>
) : (
project.description
)}
</p>
</div>
</a>
))}
</div>
);
}

View 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;
}

View 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>
);
}

View 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;
}
}

View 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>
);
}

View 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;
}

View 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>
);
}

View file

@ -0,0 +1,2 @@
.Time {
}

View 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>
);
}

View 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;
}
}

View 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
View 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
View 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
View 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;