Compare commits

...

3 Commits

Author SHA1 Message Date
pegasust 064a9355e6 WIP on theme selector of about-me, docs: updated README.mds 2022-12-01 07:53:17 +00:00
pegasust 70f7b14a67 wip: theme 2022-11-20 04:37:35 +00:00
pegasust df126da2a6 add basic components 2022-11-17 02:55:56 +00:00
8 changed files with 358 additions and 308 deletions

View File

@ -5,7 +5,6 @@ Scaffolded by `pnpm create turbo@latest`
This project manages my homelab infrastructure under a monorepo to keep things This project manages my homelab infrastructure under a monorepo to keep things
centralized and easily sharable across different modules centralized and easily sharable across different modules
# Turbo-repo details # Turbo-repo details
## What's inside? ## What's inside?
@ -14,11 +13,11 @@ This turborepo uses [pnpm](https://pnpm.io) as a package manager. It includes th
### Apps and Packages ### Apps and Packages
- `docs`: a [Next.js](https://nextjs.org/) app - `apps/docs`: a [Next.js](https://nextjs.org/) app
- `web`: another [Next.js](https://nextjs.org/) app - `apps/web`: another [Next.js](https://nextjs.org/) app
- `ui`: a stub React component library shared by both `web` and `docs` applications - `packages/ui`: a stub React component library shared by both `web` and `docs` applications
- `eslint-config-custom`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`) - `packages/eslint-config-custom`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
- `tsconfig`: `tsconfig.json`s used throughout the monorepo - `packages/tsconfig`: `tsconfig.json`s used throughout the monorepo
Each package/app is 100% [TypeScript](https://www.typescriptlang.org/). Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).

View File

@ -0,0 +1,12 @@
import { ReactNode } from "react"
export const Section: React.FC<{children?: ReactNode}> = ({children}) => <section className="text-gray-700 max-w-4xl">{children}</section>
export const UL: React.FC<{ children?: ReactNode }> = ({ children }) => <ul className="list-disc list-inside">{children}</ul>
export const SectionHeader: React.FC<{ text?: string, children?: ReactNode }> = ({ text, children }) => <div>
<h1 className="text-xl text-gray-600 tracking-widest">
{text}
{children}
</h1>
<div className="mt-8" />
</div>

View File

@ -1,15 +0,0 @@
import { Head } from "next/document";
import { Main } from "next/document";
import { NextScript } from "next/document";
import { Html } from "next/document";
import React from "react";
const document = () => <Html>
<Head />
<body className="scrollbar-track-gray-50 scrollbar-thumb-amber-400">
<Main />
<NextScript />
</body>
</Html>
export default document;

View File

View File

View File

@ -1,7 +1,7 @@
import { type NextPage } from "next"; import { type NextPage } from "next";
import Head from "next/head"; import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import React from "react"; import React, { useContext, useEffect, useState } from "react";
import { cva } from "class-variance-authority"; import { cva } from "class-variance-authority";
import type { VariantProps } from "class-variance-authority"; import type { VariantProps } from "class-variance-authority";
import Link from "next/link"; import Link from "next/link";
@ -18,6 +18,42 @@ import Lizzie from "@public/assets/lizzie.jpg";
import SantaMonicaStairs from "@public/assets/santa-monica-stairs.jpg"; import SantaMonicaStairs from "@public/assets/santa-monica-stairs.jpg";
import SantaMonicaHigh from "@public/assets/santa-monica-high.jpg"; import SantaMonicaHigh from "@public/assets/santa-monica-high.jpg";
import assert from "assert"; import assert from "assert";
import { Section, SectionHeader, UL } from "@components/index";
type Theme = "dark" | "light";
const ThemeContext = React.createContext<{
theme: Theme,
systemTheme: Theme,
setThemePreference(theme: Theme): void,
} | undefined>(undefined);
const ThemeProvider: React.FC<{ children: React.ReactNode, prefer?: "light" | "dark" }> = ({ children, prefer }) => {
const pref = prefer ?? "light";
const [systemTheme, setSystemTheme] = useState<Theme>(pref);
const [themePref, setThemePref] = useState<Theme | undefined>(undefined);
// add a listener to system preference on which theme
// TODO: is there another way that remove event listener faster?
useEffect(() => {
console.log("useEffect called");
type EventType = { matches: boolean };
const onThemeChange = (event: EventType) => {
const newColorScheme = event.matches ? "dark" : "light";
setSystemTheme(newColorScheme);
};
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', onThemeChange);
return () => {
console.log("useEffect unmount");
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener("change", onThemeChange)
}
}, []);
return <ThemeContext.Provider value={{
theme: themePref ?? systemTheme,
systemTheme: systemTheme,
setThemePreference: setThemePref,
}}>{children}</ThemeContext.Provider>
}
const useTheme = () => useContext(ThemeContext);
const circle = cva("absolute rounded-full border -z-10", { const circle = cva("absolute rounded-full border -z-10", {
variants: { variants: {
@ -33,15 +69,42 @@ const circle = cva("absolute rounded-full border -z-10", {
pulse: "animate-pulse", pulse: "animate-pulse",
ping: "animate-ping", ping: "animate-ping",
}, },
theme: {
light: "",
dark: "",
},
color: { color: {
back: "border-pink-50", back: "",
fore: "border-amber-300", fore: "",
} }
}, },
compoundVariants: [
{
theme: "light",
color: "back",
className: "border-pink-50"
},
{
theme: "light",
color: "fore",
className: "border-amber-300"
},
{
theme: "dark",
color: "back",
className: "border-violet-300"
},
{
theme: "dark",
color: "fore",
className: "border-amber-400"
},
],
defaultVariants: { defaultVariants: {
size: 0, size: 0,
animate: "none", animate: "none",
color: "back" color: "back",
theme: "light"
} }
}); });
@ -64,14 +127,14 @@ const Article: React.FC<{ children?: ReactNode }> = ({ children }) => <article
</article> </article>
const InpageSection: React.FC<{href: string, children?: ReactNode}>= ({ const InpageSection: React.FC<{ href: string, children?: ReactNode }> = ({
href, href,
children children
}) => <Link href={href} className="p-2 text-lg text-gray-500/50 rounded-3xl }) => <Link href={href} className="p-2 text-lg text-gray-500/50 rounded-3xl
border border-amber-400/50 hover:text-gray-500 hover:border-amber-400 transition-colors border border-amber-400/50 hover:text-gray-500 hover:border-amber-400 transition-colors
hover:animate-pulse"> hover:animate-pulse">
{children} {children}
</Link> </Link>
const Brief = () => <Article> const Brief = () => <Article>
{/*Avatar*/} {/*Avatar*/}
@ -101,13 +164,6 @@ const Brief = () => <Article>
</div> </div>
</Article> </Article>
const SectionHeader: React.FC<{ text?: string, children?: ReactNode }> = ({ text, children }) => <div>
<h1 className="text-xl text-gray-600 tracking-widest">
{text}
{children}
</h1>
<div className="mt-8" />
</div>
const images = [ const images = [
{ {
@ -216,7 +272,9 @@ const ImageDisplay: React.FC<{ imageDisplayHook: ImageDisplayHook }> = ({ imageD
// setImageRepo, // setImageRepo,
imgIndex, imgIndex,
image image
} }) => <div className="relative bg-gray-100/30 rounded-md overflow-hidden"> } }) => {
const {theme} = useTheme()!;
return <div className="relative h-fit w-fit bg-gray-100/30 rounded-md">
<button className="group left-0 absolute w-5/12 h-full flex flex-row justify-start items-center" onClick={prev}> <button className="group left-0 absolute w-5/12 h-full flex flex-row justify-start items-center" onClick={prev}>
<span className={imageButton()}>{"<"}</span> <span className={imageButton()}>{"<"}</span>
</button> </button>
@ -227,16 +285,18 @@ const ImageDisplay: React.FC<{ imageDisplayHook: ImageDisplayHook }> = ({ imageD
<div className="flex flex-row justify-between items-center gap-2"> <div className="flex flex-row justify-between items-center gap-2">
{Array.from(Array(imageRepo.length).keys()).map(i => <div className={imageIndex({ {Array.from(Array(imageRepo.length).keys()).map(i => <div className={imageIndex({
active: (i == imgIndex ? "isActive" : "isInactive"), active: (i == imgIndex ? "isActive" : "isInactive"),
theme: "light" theme: theme
})} })}
key={`img-index-${i}`} />)} key={`img-index-${i}`} />)}
</div> </div>
</div> </div>
{image && <Image src={image.src} alt={image.alt} className="w-96 h-96 object-contain rounded-md overflow-hidden transition-all" />} {image && <Image placeholder="blur" src={image.src} alt={image.alt}
className="w-96 h-96 object-contain rounded-md" />}
</div> </div>
}
const MyLink: React.FC<{ href: string, children?: ReactNode }> = ({ href, children }) => <Link const MyLink: React.FC<{ href: string, children?: ReactNode }> = ({ href, children }) => <Link
className="text-amber-400/70 visited:text-purple-300/70 hover:text-amber-400 visited:hover:text-purple-300" href={href}> className="text-amber-400/70 visited:text-purple-400/70 hover:text-amber-400 visited:hover:text-purple-400" href={href}>
{children} {children}
</Link> </Link>
@ -245,36 +305,36 @@ const About = () => {
return <Article> return <Article>
<SectionHeader>About</SectionHeader> <SectionHeader>About</SectionHeader>
<div className="flex flex-row gap-4"> <div className="flex flex-row w-11/12 justify-evenly max-w-screen-lg gap-8">
<ImageDisplay imageDisplayHook={imgDisplay} /> <ImageDisplay imageDisplayHook={imgDisplay} />
<div className="text-left"> <div className="text-left">
<section> <Section>
<span> <span>
Aspiring software engineer with intense drive and curiosity in software development. Aspiring software engineer with intense drive and curiosity in software development.
</span> </span>
</section> </Section>
<section className="max-w-4xl"> <Section>
My computer infrastructure consists of: My computer infrastructure consists of:
<ul className="list-disc list-inside"> <UL>
<li>24 home CPU cores</li> <li>24 home CPU cores</li>
<li>60 GB of RAM (4 GB SDDR3 and 56 GB DDR4)</li> <li>60 GB of RAM (4 GB SDDR3 and 56 GB DDR4)</li>
<li>2.5 TB of SSD + HDD</li> <li>2.5 TB of SSD + HDD</li>
<li>A mix of NixOS, Ubuntu, and Windows</li> <li>A mix of NixOS, Ubuntu, and Windows</li>
</ul> </UL>
</section> </Section>
<section> <Section>
<span> <span>
In free time, you would find me: In free time, you would find me:
</span> </span>
<ul className="list-disc list-inside"> <UL>
<li>Learning about new technology in a week</li> <li>Learning about new technology in a week</li>
<li>Performing microbenchmarks on competing implementations and technologies</li> <li>Performing microbenchmarks on competing implementations and technologies</li>
<li>Optimizing my workspace through <MyLink href="https://git.pegasust.com/pegasust/dotfiles.git">dotfiles</MyLink></li> <li>Optimizing my workspace through <MyLink href="https://git.pegasust.com/pegasust/dotfiles.git">dotfiles</MyLink></li>
<li>Contributing to open-source projects</li> <li>Contributing to open-source projects</li>
<li>Watching edutainment videos and podcasts about software</li> {/*TODO: add link to edutainment note-taking platform here*/} <li>Watching edutainment videos and podcasts about software</li> {/*TODO: add link to edutainment note-taking platform here*/}
<li><MyLink href="https://soundcloud.com/h-ng-tr-n-186908751">Producing music</MyLink></li> <li><MyLink href="https://soundcloud.com/h-ng-tr-n-186908751">Producing music</MyLink></li>
</ul> </UL>
</section> </Section>
</div> </div>
</div> </div>
</Article> </Article>
@ -307,7 +367,7 @@ const NavbarItem: React.FC<{
</Link> </Link>
const iconCva = cva("transition-colors", { const iconCva = cva("transition-colors group-hover:animate-bounce", {
variants: { variants: {
theme: { theme: {
light: "group-hover:fill-gray-700 fill-gray-400" light: "group-hover:fill-gray-700 fill-gray-400"
@ -318,21 +378,26 @@ const iconCva = cva("transition-colors", {
} }
}); });
const Navbar = () => <div className="flex justify-center items-center w-full z-50 sticky top-0 left-0 backdrop-blur-md h-8"> const Navbar = () => {
const _theme = useTheme();
if (!_theme) { throw new Error("no theme provider"); }
const { theme, setThemePreference } = _theme;
return <div className="flex justify-center items-center w-full z-50 sticky top-0 left-0 backdrop-blur-md h-8">
<div className="w-11/12 max-w-screen-lg flex items-center gap-4"> <div className="w-11/12 max-w-screen-lg flex items-center gap-4">
<div className="flex flex-row justify-center items-center gap-1"> <div className="flex flex-row justify-center items-center gap-1">
<NavbarItem text="GitHub" link="https://github.com/pegasust"><FaGithub className={iconCva()} /></NavbarItem> <NavbarItem text="GitHub" link="https://github.com/pegasust"><FaGithub className={iconCva()} /></NavbarItem>
<NavbarItem text="LinkedIn" link="https://linkedin.com/in/hung-tran-1963bb205/"><FaLinkedin className={iconCva()} /></NavbarItem> <NavbarItem text="LinkedIn" link="https://linkedin.com/in/hung-tran-1963bb205/"><FaLinkedin className={iconCva()} /></NavbarItem>
</div> </div>
<Link href="#brief"> <Link href="#brief">
<span className="transition-colors hover:text-amber-400 text-amber-400/50 animate-pulse">Hung Tran</span> <span className="transition-colors hover:text-amber-400 text-amber-400/50 hover:animate-pulse">Hung Tran</span>
</Link> </Link>
</div> </div>
</div> </div>
}
const Home: NextPage = () => { const Home: NextPage = () => {
return ( return (
<ThemeProvider>
<main className="overflow-x-hidden z-0 flex flex-col overflow-y-scroll h-screen <main className="overflow-x-hidden z-0 flex flex-col overflow-y-scroll h-screen
items-center bg-pink-50/20 min-h-screen antialiased snap-y snap-proximity items-center bg-pink-50/20 min-h-screen antialiased snap-y snap-proximity
scrollbar-track-gray-200 scrollbar-thumb-amber-300/50 scrollbar-thin"> {/*vimium users cry if snap-mandatory*/} scrollbar-track-gray-200 scrollbar-thumb-amber-300/50 scrollbar-thin"> {/*vimium users cry if snap-mandatory*/}
@ -358,6 +423,7 @@ const Home: NextPage = () => {
<Projects /> <Projects />
</section> </section>
</main> </main>
</ThemeProvider>
); );
}; };

View File

@ -12,8 +12,9 @@
"@server/*": ["src/server/*"], "@server/*": ["src/server/*"],
"@db/*": ["src/server/db/*"], "@db/*": ["src/server/db/*"],
"@trpc/*": ["src/server/trpc/*"], "@trpc/*": ["src/server/trpc/*"],
"@router/*": ["src/server/trpc/router"], "@router/*": ["src/server/trpc/router/*"],
"@public/*": ["public/*"] "@public/*": ["public/*"],
"@components/*": ["src/components/*"]
} }
} }
} }

View File

@ -4,19 +4,6 @@
"extends": "./nextjs.json", "extends": "./nextjs.json",
"compilerOptions": { "compilerOptions": {
"declaration": false, "declaration": false,
"baseUrl": ".",
"paths": {
"@env/*": ["src/env/*"],
"@pages/*": ["src/pages/*"],
"@styles/*": ["src/styles/*"],
"@utils/*": ["src/utils/*"],
"@server/*": ["src/server/*"],
"@db/*": ["src/server/db/*"],
"@trpc/*": ["src/server/trpc/*"],
"@router/*": ["src/server/trpc/router"],
"@public/*": ["public/*"]
}
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"],
"exclude": ["node_modules"], "exclude": ["node_modules"],