Merge pull request #1 from innocuous-symmetry/next-migration

Next migration
This commit is contained in:
Mikayla Dobson
2023-08-02 17:40:08 -05:00
committed by GitHub
97 changed files with 1548 additions and 4453 deletions

6
.eslintrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": "next/core-web-vitals",
"rules": {
"import/no-anonymous-default-export": "off"
}
}

20
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: build check (mikayla dot dev)
on:
push:
branches:
- master
- next-migration
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
with:
ref: master
- name: install
run: npm install
- name: build check
run: npm run build

52
.gitignore vendored
View File

@@ -1,25 +1,37 @@
# Logs # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
logs
*.log # dependencies
/node_modules
/.pnp
.pnp.js
package-lock.json
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
*.env
# debug
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules # local env files
dist .env*.local
dist-ssr
*.local
package-lock.json
# Editor directories and files # vercel
.vscode/* .vercel
!.vscode/extensions.json
.idea # typescript
.DS_Store *.tsbuildinfo
*.suo next-env.d.ts
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -1,5 +1,34 @@
# Mikayla Dobson, Web Developer, Software Engineer, Musician This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
Thanks for checking out my portfolio site! This repo has been migrated over from an [older version](https://github.com/innocuous-symmetry/about-mikayla) which is no longer being maintained. This version will stay up to date with my latest projects and experience! ## Getting Started
Created using React, Sass, Material UI, and Vite. Hosted with Github pages. First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -0,0 +1,3 @@
export default function EducationPage() {
return <>Education Page</>
}

79
app/about/me/content.mdx Normal file
View File

@@ -0,0 +1,79 @@
# What I Do
## CREATE FULL STACK WEB APPLICATIONS
I have experience building web applications with and without back-end integrations.
I am comfortable conceptualizing and organizing complex structures, and as such, my projects tend to be natural in their structure and easy to maintain.
## COLLABORATIVE SOFTWARE ENGINEERING
I have consulted on small teams with:
- Dization, a Pittsburgh-based company developing an enterprise resource planning solution for small businesses
- Metazu Studio, a Nashville-based startup connecting clients with services in video production, AR/VR, social media, photography, and web design.
My work on these projects deals with managing the complexities of full-stack web engineering, as well as delivering beautiful user experiences to end customers.
## CREATIVE MINDED PROBLEM SOLVER
My background as a musician, composer, producer, and artistic collaborator provide me with a unique frame of reference for solving technical problems and adapting to dynamic environments.
## DATABASE OPERATIONS AND MANAGEMENT
My projects have featured both relational and non-relational databases, in particular PostgreSQL and MongoDB. I also have experience with various methods of connecting these to front-end applications.
# Education
## BACHELORS OF ARTS, MUSIC
Southern Methodist University, 2014 - 2017
Concentrations: Piano Performance, Music Composition, Music Theory
Minor: French Language and Culture
## SELF DIRECTED STUDY, WEB ENGINEERING
2021 - present
Codecademy Pro, Front End Engineer Career Path
Concentrations: React, Redux, Express, PostgreSQL
# Employment
## SOFTWARE ENGINEER
Dropper Studio | Nashville, TN
Mar 2023 - present
Relevant Duties: developing a full-stack web application for a client in the music industry.
- Next.js
- tRPC, MongoDB
- Integrating additional services (Redis, S3)
- Networking and cloud management (on premise)
- UI/UX Design
## FULL STACK ENGINEER, INTERN
Dization, Inc | Pittsburgh, PA (remote)
Sept 2022 - Mar 2023
Relevant Duties: contributing to the development of a full-stack enterprise resource planning application built using the LAMP stack hosted on AWS.
- Enterprise Resource Planning
- AWS
- System Design
- UI/UX Design
- Database management
## FULL STACK ENGINEER, INTERN
Dization, Inc | Pittsburgh, PA (remote)
Sept 2022 - present
Relevant Duties: contributing to the development of a full-stack enterprise resource planning application built using the LAMP stack hosted on AWS.
Enterprise Resource Planning
AWS
System Design
UI/UX Design
Database management

41
app/about/me/page.tsx Normal file
View File

@@ -0,0 +1,41 @@
'use client';
import { useState } from "react";
import Software from "./software.mdx";
import CompanyShowcase from "@/components/About/CompanyShowcase";
export default function AboutMe() {
const [selected, setSelected] = useState<'musician' | 'developer'>();
return (
<div>
<h1>This page is also coming soon!</h1>
{/* <h1>So, who is Mikayla Dobson?</h1>
<div>
<button onClick={() => setSelected('developer')}>Software Engineer</button>
<button onClick={() => setSelected('musician')}>Musician</button>
</div>
{selected === 'developer' && (
<div>
<p>
I have been writing software for about three years, and have produced valuable work for several
companies working in a variety of paradigms.
</p>
<p>Here are some traits that my supervisors have consistently praised:</p>
</div>
)}
{selected === 'musician' && (
<div>
<h2>Musician</h2>
<p>
I am a musician with a passion for creating beautiful, functional, and accessible music.
I have experience with the piano, guitar, and more.
</p>
</div>
)} */}
</div>
)
}

10
app/about/me/software.mdx Normal file
View File

@@ -0,0 +1,10 @@
# Software Engineer
I have been writing software for about three years, and have produced valuable work for several
companies working in a variety of paradigms.
## Some
## Some ways I have brought value:

19
app/about/music/page.tsx Normal file
View File

@@ -0,0 +1,19 @@
import Link from 'next/link';
export default function MusicPage() {
return (
<div>
<Link href="/about/music/projects">
<p>Projects</p>
</Link>
<Link href="/about/music/works">
<p>Works</p>
</Link>
<Link href="/about/music/stream">
<p>Stream</p>
</Link>
</div>
)
}

View File

@@ -0,0 +1,9 @@
export default function MusicProjectPage() {
return (
<div>
<h1>Music Project Page</h1>
<p>This is where I&apos;ll keep a running list of my projects</p>
</div>
)
}

View File

@@ -0,0 +1,7 @@
export default function MusicStreamingPage() {
return (
<div>
<h1>Music Streaming Page</h1>
</div>
)
}

View File

@@ -0,0 +1,16 @@
export default function MusicalWorkPage({ params }: { params: { id: string }}) {
if (Number.isNaN(parseInt(params.id))) {
return <div>Fail</div>
}
return (
<div>
<h1>Music Works Page</h1>
<p>Work No. {params.id}</p>
<div>
<p>This page is coming soon!</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,21 @@
import Link from 'next/link';
export default function MusicWorksPage() {
return (
<div>
<h1>Music Works Page</h1>
<Link href="/about/music/works/1">
<p>First</p>
</Link>
<Link href="/about/music/works/2">
<p>Second</p>
</Link>
<Link href="/about/music/works/3">
<p>Third</p>
</Link>
</div>
)
}

79
app/about/page.tsx Normal file
View File

@@ -0,0 +1,79 @@
"use client";
import Link from "next/link";
import { Experience, Projects, Skills } from "../../components/Resume";
import { RxChevronDown } from "react-icons/rx";
import { useState } from "react";
import { FaGithub, FaLinkedin } from "react-icons/fa";
export default function Resume() {
const [skillsVisible, setSkillsVisible] = useState(true);
const [experienceVisible, setExperienceVisible] = useState(true);
const [educationVisible, setEducationVisible] = useState(true);
const [projectsVisible, setProjectsVisible] = useState(true);
return (
<div className="min-h-screen bg-gradient-to-b from-black to-slate-950 bg-fixed flex flex-col items-center p-4 z-10">
<section className="w-full pb-4">
<h1 className="uppercase text-4xl text-rose-300 tracking-wide leading-relaxed font-light">Mikayla Dobson</h1>
<h2 className="text-2xl tracking-wide font-light">Software Engineer | Nashville, TN</h2>
<div className="hidden bg-slate-950"></div>
<div className="flex items-center pt-2">
<p className="mr-2">Download a copy for later?</p>
<a download href="/resume/Mikayla Resume 0623.pdf" target="_blank" referrerPolicy="no-referrer" rel="noopener" className="mr-2 cursor-pointer hover:text-sky-300 active:text-white bg-slate-800 p-1 rounded-lg">.pdf</a>
<a download href="/resume/Mikayla Resume 0623.docx" target="_blank" referrerPolicy="no-referrer" rel="noopener" className="mr-2 cursor-pointer hover:text-sky-300 active:text-white bg-slate-800 p-1 rounded-lg">.docx</a>
</div>
<div className="flex pt-2">
<Link passHref className="flex items-center mr-8 hover:text-sky-300 active:text-white" href="https://github.com/innocuous-symmetry"><FaGithub className="mr-2" />innocuous-symmetry</Link>
<Link passHref className="flex items-center mr-8 hover:text-sky-300 active:text-white" href="https://linkedin.com/in/mikayla-dobson"><FaLinkedin className="mr-2" />mikayla-dobson</Link>
</div>
</section>
<section className="place-self-start mt-8 w-full">
<button onClick={() => setSkillsVisible(!skillsVisible)} className={"sticky top-[5.5rem] w-full flex items-center text-rose-300 uppercase tracking-wide list-none" + (skillsVisible ? " bg-black" : "")}>
<span className="font-light text-3xl leading-relaxed">Skills</span>
<RxChevronDown className={`ml-2 transition ${skillsVisible ? "rotate-180" : "rotate-0"}`} />
</button>
{ skillsVisible ? <Skills /> : null }
</section>
<section className="place-self-start mt-8 w-full">
<button onClick={() => setExperienceVisible(!experienceVisible)} className="sticky top-[5.5rem] w-full bg-black flex items-center text-rose-300 uppercase tracking-wide list-none">
<span className="font-light text-3xl leading-relaxed">Experience</span>
<RxChevronDown className={`ml-2 transition ${experienceVisible ? "rotate-180" : "rotate-0"}`} />
</button>
{ experienceVisible ? <Experience /> : null }
</section>
<section className="place-self-start mt-8 w-full">
<button onClick={() => setEducationVisible(!educationVisible)} className={"sticky top-[5.5rem] w-full flex items-center text-rose-300 uppercase tracking-wide list-none" + (educationVisible ? " bg-black" : "")}>
<h2 className="font-light text-3xl leading-relaxed">Education</h2>
<RxChevronDown className={`ml-2 transition ${educationVisible ? "rotate-0" : "rotate-180"}`} />
</button>
{
educationVisible ? (
<article className={(educationVisible ? "h-auto my-4 opacity-100 bg-black" : "h-0 opacity-0 my-0") + " transition duration-500 bg-opacity-40 shadow shadow-black p-3 rounded-lg"}>
<h3 className="uppercase text-2xl text-rose-600">Southern Methodist University</h3>
<p className="font-light italic text-rose-300">Dallas, TX - B.A. Music</p>
<div className="h-[1px] w-full my-3 bg-rose-300" />
<p className="leading-relaxed font-light">Concentrations in Piano Performance and Music Composition. Special focus on orchestral composition and arranging. Minor in French Language and Culture.</p>
</article>
) : null
}
</section>
<section className="place-self-start mt-8 w-full">
<button onClick={() => setProjectsVisible(!projectsVisible)} className={"sticky top-[5.5rem] w-full flex items-center text-rose-300 uppercase tracking-wide list-none " + (projectsVisible ? " bg-black" : "")}>
<h2 className="font-light text-3xl leading-relaxed">Projects</h2>
<RxChevronDown className={`ml-2 transition ${projectsVisible ? "rotate-180" : "rotate-0"}`} />
</button>
{ projectsVisible ? <Projects /> : null }
</section>
</div>
)
}

View File

@@ -0,0 +1,18 @@
'use client';
import { usePathname } from "next/navigation";
export default function ExperiencePage() {
const path = usePathname();
return (
<div>
<div id="spacer" className='h-[6rem] w-full' />
<h1>Work Page</h1>
<p>Employer: {path.split('/').at(-1)}</p>
<div>
<p>This section is coming soon!</p>
</div>
</div>
)
}

7
app/about/work/page.tsx Normal file
View File

@@ -0,0 +1,7 @@
const WorkHistory = () => {
return (
<div>
<h1>Work History</h1>
</div>
);
}

42
app/contact/page.tsx Normal file
View File

@@ -0,0 +1,42 @@
'use client';
import { useState } from "react";
export default function ContactPage() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
console.log(name, email, message);
}
return (
<div className="min-w-screen min-h-screen bg-gradient-to-b from-black to-darkPlum">
<div className="flex flex-col mx-24 items-center">
<h1 className="text-3xl my-8 place-self-start">Thanks for your interest! I&apos;m looking forward to hearing from you.</h1>
<form onSubmit={handleSubmit} className="w-full">
<div className="flex w-full">
<div className="flex flex-col w-1/2 mr-2">
<label htmlFor="name">Name</label>
<input className="bg-neutral-100 px-1.5 py-1 text-black" value={name} onChange={(e) => setName(e.target.value)} type="text" name="name" id="name" />
</div>
<div className="flex flex-col w-1/2 ml-2">
<label htmlFor="email">Email</label>
<input className="bg-neutral-100 px-1.5 py-1 text-black" value={email} onChange={(e) => setEmail(e.target.value)} type="email" name="email" id="email" />
</div>
</div>
<div className="flex flex-col w-full mt-4">
<label htmlFor="message">Message</label>
<textarea className="bg-neutral-100 px-1.5 py-1 text-black" value={message} onChange={(e) => setMessage(e.target.value)} name="message" id="message" cols={30} rows={5}></textarea>
</div>
<button className="p-2 px-8 mt-8 rounded-lg bg-rose-300 hover:bg-rose-400 text-black" type="submit">Send!</button>
</form>
</div>
</div>
)
}

23
app/content.mdx Normal file
View File

@@ -0,0 +1,23 @@
# Mikayla Dobson | Software Engineer
Software design at the confluence of efficiency, ingenuity, and artistry.
## Why do you want me on your team?
Here are some things I've provided for my teams in the past:
### Connecting the dots
My strength lies in the big picture. My background as a composer of classical music has given me a lasting sense for how to assemble the pieces of a project into a cohesive whole.
As a software engineer, this means:
- I'm comfortable living in the world between the front-end and back-end
- I love solving emergent problems as a project's stack is expanding
---
## Want to know more?
You're in the right place! This site is the best place to learn about the work I've done, what I know, and what I've
done with the knowledge I have.

BIN
app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

27
app/globals.css Normal file
View File

@@ -0,0 +1,27 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}

78
app/layout.tsx Normal file
View File

@@ -0,0 +1,78 @@
'use client'
import './globals.css'
import Head from 'next/head'
import Navbar from '@/components/Navbar'
import SiteTree from '@/components/SiteTree'
import components from '@/components/mdx'
import { MDXProvider } from '@mdx-js/react'
import { Inter, Besley, Cabin } from 'next/font/google'
import { usePathname } from 'next/navigation'
import { IconContext } from 'react-icons'
import { useEffect, useState } from 'react'
export const inter = Inter({ subsets: ['latin'] })
export const besley = Besley({ subsets: ['latin'] })
export const cabin = Cabin({ subsets: ['latin'] })
export const metadata = {
title: 'Mikayla Dobson | Software Engineer',
description: 'Integrating artistry and technology to create beautiful software',
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const [bg, setBg] = useState('bg-slate-900');
const [overlay, setOverlay] = useState(false);
const [pageIsScrolled, setPageIsScrolled] = useState(false);
useEffect(() => {
switch (pathname) {
case '/contact':
setBg('bg-darkPlum');
setOverlay(true);
break;
case '/':
case '/about':
default:
setBg('bg-slate-900');
setOverlay(false);
break;
}
}, [pathname])
useEffect(() => {
document.addEventListener('scroll', () => {
if (window.scrollY > 0) {
setPageIsScrolled(true);
} else {
setPageIsScrolled(false);
}
})
}, [])
return (
<html lang="en">
<Head>
<title>{metadata.title}</title>
<meta name="description" content={metadata.description} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<body className={inter.className}>
<Navbar pageIsScrolled={pageIsScrolled} />
<SiteTree />
<IconContext.Provider value={{}}>
<MDXProvider components={components}>
<main className={`${bg} min-h-screen`}>
<div id="navbar-spacer" className="h-[6rem] w-full bg-black" />
{children}
</main>
</MDXProvider>
</IconContext.Provider>
</body>
</html>
)
}

39
app/page.tsx Normal file
View File

@@ -0,0 +1,39 @@
import { ColorChangeName } from "@/components/Home";
import Image from "next/image";
import Link from "next/link";
export default function Home() {
return (
<div className="flex flex-col w-full">
<div id="hero" className="bg-gradient-to-b from-black to-slate-900 p-4 flex flex-col items-end">
<ColorChangeName />
{/* <div className="w-full h-[10vh] object-scale-down bg-no-repeat">
<Image fill src="/backdrops/jean-beller-peW5dg2-cLI-unsplash.jpg" alt="geometric pattern in wood and metal" />
</div> */}
</div>
<div className="flex flex-col items-start">
<div className="flex flex-col w-full sm:w-2/3 relative mt-6 sm:mt-0 sm:bottom-12 items-center text-center">
<Link passHref href="/about">
<Image className="rounded-full my-4" src="/img/profile.jpeg" alt="image of Mikayla Dobson" width={125} height={125} />
</Link>
<h2 className="text-blue-300 text-2xl mb-2">Hi! My name is Mikayla.</h2>
<p className="px-4 sm:px-12">I build full-stack software solutions for the web, answer open-ended questions, and pet cats.</p>
<div id="actions" className="flex justify-around my-8">
<Link className="flex flex-col items-center justify-center text-center px-4 sm:px-8 border-white border-[1px] p-2 w-2/5 rounded-md" href="/contact">
Tell me what I can do for you
</Link>
<Link className="hidden sm:flex flex-col items-center justify-center text-center px-8 border-white border p-2 w-2/5 rounded-md" href="/projects">
And check out some of my work while you&apos;re at it
</Link>
<Link className="sm:hidden flex flex-col items-center justify-center px-4 border-white border p-2 w-2/5 rounded-md" href="/projects">
Check out my work
</Link>
</div>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,12 @@
import { usePathname } from 'next/navigation'
export default function ProjectById() {
const pathname = usePathname();
return (
<div>
<h1>ProjectById Page</h1>
<p>Project ID: {pathname}</p>
</div>
)
}

9
app/projects/page.tsx Normal file
View File

@@ -0,0 +1,9 @@
export default function ProjectsPage() {
return (
<div>
<h1>Projects Page</h1>
<p>Contents of this page coming soon!</p>
</div>
)
}

View File

@@ -0,0 +1,28 @@
import { useEffect, useState } from "react";
import { RxTriangleDown } from "react-icons/rx";
interface CompanyShowcaseProps {
companyName: string
children: React.ReactNode
}
export default function CompanyShowcase({ companyName, children }: CompanyShowcaseProps) {
const [collapsed, setCollapsed] = useState(true);
useEffect(() => console.log(collapsed), [collapsed]);
return (
<div className="flex flex-col">
<div className="flex items-center">
<h1>{companyName}</h1>
<div className="transition-transform duration-700 cursor-pointer">
<RxTriangleDown className={collapsed ? "" : "rotate-180"} onClick={() => setCollapsed(!collapsed)} />
</div>
{/* <button onClick={() => setCollapsed(!collapsed)}>Show more</button> */}
</div>
<div className={"flex flex-col"}>
{ collapsed ? <></> : children }
</div>
</div>
)
}

20
components/Home/index.tsx Normal file
View File

@@ -0,0 +1,20 @@
'use client';
import { useColorShift } from "../logo";
export const ColorChangeName = () => {
const { firstColor, secondColor, thirdColor } = useColorShift(14000);
return (
<>
<span className={"px-4 mb-2 mt-4 bg-clip-text text-transparent uppercase text-3xl sm:text-6xl font-extrabold bg-opacity-100 animate-text-gradient bg-gradient-to-r " + `from-${firstColor.split('-').slice(1).join('-')} to-${secondColor.split('-').slice(1).join('-')}`}>
Mikayla Dobson
</span>
<h2 className={"px-4 uppercase text-2xl text-transparent font-bold bg-opacity-100 animate-text-gradient bg-clip-text bg-gradient-to-r " + `from-${secondColor.split('-').slice(1).join('-')} to-${thirdColor.split('-').slice(1).join('-')}`}>Software Engineer</h2>
<div id="hero-mask" className="bg-inherit bg-opacity-10 p-4 flex flex-col">
<p className="mt-4 text-right text-sky-300 tracking-wide">Pragmatic software design with style and artistry.</p>
</div>
</>
)
}

124
components/Navbar/index.tsx Normal file
View File

@@ -0,0 +1,124 @@
import Link from 'next/link'
import { InlineLogo, useColorShift } from '../logo'
import { useEffect, useState } from 'react';
import { UseColorShiftReturnType } from '../logo/useColorShift';
import { RxActivityLog } from "react-icons/rx";
interface HoverState {
about: boolean
projects: boolean
contact: boolean
}
const SHIFT_INTERVAL = 3000;
export default function Navbar({ pageIsScrolled = false }) {
const navbarColorShift = useColorShift(SHIFT_INTERVAL);
const { shift } = navbarColorShift;
const [colors, setColors] = useState<Partial<UseColorShiftReturnType>>({
firstColor: 'bg-inherit',
secondColor: 'bg-inherit',
thirdColor: 'bg-inherit',
});
const [hoverState, setHoverState] = useState<HoverState>({
about: false,
projects: false,
contact: false,
})
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
function mouseOver(source: keyof HoverState) {
const { colorKeys, actualColorReferences, activeIndex } = identifyActiveButton();
setColors({ ...colors, [colorKeys[activeIndex]]: actualColorReferences[activeIndex] });
setHoverState({ ...hoverState, [source]: true })
}
function mouseOut(source: keyof HoverState) {
setHoverState({ ...hoverState, [source]: false })
setColors({ firstColor: 'bg-inherit', secondColor: 'bg-inherit', thirdColor: 'bg-inherit' });
}
function identifyActiveButton() {
const buttonKeys: (keyof HoverState)[] = ['about', 'projects', 'contact'];
const { firstColor, secondColor, thirdColor } = navbarColorShift;
const colorKeys = ['firstColor', 'secondColor', 'thirdColor'];
const actualColorReferences = [firstColor, secondColor, thirdColor];
const activeButton = buttonKeys.find(key => hoverState[key]);
const activeIndex = buttonKeys.indexOf(activeButton as keyof HoverState);
return { colorKeys, actualColorReferences, activeIndex };
}
useEffect(() => {
const interval = setInterval(shift, SHIFT_INTERVAL);
return () => clearInterval(interval);
}, [])
useEffect(() => {
const interval = setInterval(() => {
const { colorKeys, actualColorReferences, activeIndex } = identifyActiveButton();
for (const key in hoverState) {
if (hoverState[key as keyof HoverState]) {
setColors({
...colors,
[colorKeys[activeIndex]]: actualColorReferences[activeIndex]
});
}
}
}, 1000);
return () => clearInterval(interval);
}, [shift])
return (
<>
<div id="navbar" className={"w-full z-50 fixed flex flex-nowrap items-baseline justify-apart bg-opacity-95 px-8 py-4 " + (mobileMenuOpen ? "bg-[#131313] " : pageIsScrolled ? "bg-black " : "bg-inherit ") + " text-white transition-all duration-200"}>
<Link passHref href="/" className="w-1/4">
<InlineLogo customHookInstance={navbarColorShift} />
</Link>
<div className="hidden md:inline-flex justify-end w-3/4">
<Link passHref href="/about" onMouseOver={() => mouseOver('about')} onMouseOut={() => mouseOut('about')} className={`ml-2 ${colors.firstColor} rounded-lg transition-colors ease-quick-start duration-${hoverState.about ? '[5000ms]' : '0'}`}>
<p className='text-lg text-white text-opacity-80 hover:text-opacity-100 uppercase border-white border-2 p-2 rounded-lg border-opacity-50 hover:border-opacity-75'>About</p>
</Link>
<Link passHref href="/projects" onMouseOver={() => mouseOver("projects")} onMouseOut={() => mouseOut('projects')} className={`ml-2 ${colors.secondColor} rounded-lg transition-colors ease-quick-start duration-${hoverState.projects ? '[5000ms]' : '0'}`}>
<p className='text-lg text-white text-opacity-80 hover:text-opacity-100 hover:border-opacity-75 uppercase border-white border-2 p-2 rounded-lg border-opacity-50'>Projects</p>
</Link>
<Link passHref href="/contact" onMouseOver={() => mouseOver('contact')} onMouseOut={() => mouseOut('contact')} className={`ml-2 ${colors.thirdColor} rounded-lg transition-colors ease-quick-start duration-${hoverState.contact ? '[5000ms]' : '0'}`}>
<p className='text-lg text-white text-opacity-80 hover:text-opacity-100 uppercase border-white border-2 p-2 rounded-lg border-opacity-50 hover:border-opacity-75'>Contact</p>
</Link>
</div>
<div aria-expanded={mobileMenuOpen} aria-roledescription="mobile-only navbar" className="inline-flex md:hidden justify-end h-full w-3/4">
<button className={(mobileMenuOpen ? "bg-[#131313]" : "bg-inherit")} onClick={() => setMobileMenuOpen(!mobileMenuOpen)}>
<RxActivityLog size="24" />
</button>
</div>
</div>
<div onMouseLeave={() => setMobileMenuOpen(false)} className={`flex flex-col z-50 rounded-bl-lg justify-end md:hidden fixed top-24 w-[35vw] text-right place-self-end bg-[#131313] ${mobileMenuOpen ? 'translate-x-[65vw]' : 'translate-x-[100vw]'} transition-all duration-500`}>
{/* <div className="bg-black h-48" /> */}
<Link onClick={() => setMobileMenuOpen(false)} passHref href="/" className="w-auto px-2">
<p className='text-lg text-right text-white text-opacity-80 hover:text-opacity-100 uppercase p-2 border-opacity-50 hover:border-opacity-75'>Home</p>
</Link>
<Link onClick={() => setMobileMenuOpen(false)} passHref href="/about" className="w-auto px-2">
<p className='text-lg text-right text-white text-opacity-80 hover:text-opacity-100 uppercase p-2 border-opacity-50 hover:border-opacity-75'>About</p>
</Link>
<Link onClick={() => setMobileMenuOpen(false)} passHref href="/projects" className="w-auto px-2">
<p className='text-lg text-right text-white text-opacity-80 hover:text-opacity-100 hover:border-opacity-75 uppercase p-2 border-opacity-50'>Projects</p>
</Link>
<Link onClick={() => setMobileMenuOpen(false)} passHref href="/contact" className="w-auto px-2">
<p className='text-lg text-right text-white text-opacity-80 hover:text-opacity-100 uppercase p-2 border-opacity-50 hover:border-opacity-75'>Contact</p>
</Link>
</div>
</>
)
}

View File

@@ -0,0 +1,45 @@
import Link from "next/link";
const Experience = () => {
return (
<section className="w-full">
<article className="bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<Link target="_blank" referrerPolicy="no-referrer" href="https://dropper.studio" className="uppercase text-2xl text-rose-600 hover:text-rose-400 active:text-rose-600">Dropper Studio</Link>
<p className="font-light italic text-rose-300">Nashville, TN (hybrid) - Software Engineer</p>
<p>March 2023 - present</p>
<div className="h-[1px] w-full my-3 bg-rose-300" />
<p className="leading-relaxed font-light">Building a full-stack e-commerce platform for the music industry. Experience includes: producing a functional proof of concept from design specifications; constructing a scalable, performant full-stack architecture; and project/team management skills.</p>
<Link href="/about/work/dropper" className="text-rose-300 hover:text-rose-500 active:text-rose-300">Learn more about my work with Dropper</Link>
</article>
<article className="bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<Link target="_blank" referrerPolicy="no-referrer" href="https://dization.com/" className="uppercase text-2xl text-rose-600 hover:text-rose-400 active:text-rose-600">Dization, Inc.</Link>
<p className="font-light italic text-rose-300">Pittsburgh, PA (remote) - Software Engineer (intern)</p>
<p>October 2022 - March 2023</p>
<div className="h-[1px] w-full my-3 bg-rose-300" />
<p className="leading-relaxed font-light">Participated in the development of an enterprise project management solution. Built several new features, contributed to design and UX discussions, and took initiative on debugging efforts. Technologies included PHP, MySQL, jQuery, and Bootstrap.</p>
<Link href="/about/work/dization" className="text-rose-300 hover:text-rose-500 active:text-rose-300">Learn more about my work with Dization</Link>
</article>
<article className="bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<h3 className="uppercase text-2xl text-rose-600">Metazu Studio</h3>
<p className="font-light italic text-rose-300">Nashville, TN (hybrid) - Software Engineer (consultant)</p>
<p>March 2022 - December 2022</p>
<div className="h-[1px] w-full my-3 bg-rose-300" />
<p className="leading-relaxed font-light">Consulted on small teams for the design and engineering of full-stack web applications for clients. Used technologies including Node.js, React, MongoDB, and PostgreSQL.</p>
</article>
<Link href="/about/work" className="text-rose-300 hover:text-rose-500 active:text-rose-300 bg-slate-950 p-2 rounded-lg shadow-lg">See more about my experience</Link>
</section>
)
}
export default Experience;

View File

@@ -0,0 +1,25 @@
const Projects = () => (
<section className="w-full">
<article className="bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<h3 className="uppercase text-2xl text-rose-600">Subsequent</h3>
<p className="font-light italic text-rose-300">May 2023 - present</p>
<p>TypeScript, Tone.js</p>
<div className="h-[1px] w-full my-3 bg-rose-300" />
<p className="leading-relaxed font-light">A tool for music creators to explore generative music composition. Composed of an engine that wraps around Tone.js, allowing the end user to specify parameters for indefinite generative music output. To be published as an NPM package, including providers to be consumed in front-end web frameworks.</p>
</article>
<article className="bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<h3 className="uppercase text-2xl text-rose-600">Recipin</h3>
<p className="font-light italic text-rose-300">October 2022 - present</p>
<p>React, Express, TypeScript, PostgreSQL</p>
<div className="h-[1px] w-full my-3 bg-rose-300" />
<p className="leading-relaxed font-light">A full stack web application for storing personal recipes in collections and sharing them in a lightweight social network.</p>
</article>
</section>
)
export default Projects

View File

@@ -0,0 +1,164 @@
import { RxChevronDown } from "react-icons/rx";
import Link from "next/link";
const Skills = () => (
<section className="w-full">
<div className="bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<h3 className="uppercase text-2xl text-rose-600">Strong:</h3>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">JavaScript</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<Link target="_blank" referrerPolicy="no-referrer" href="https://github.com/innocuous-symmetry?tab=repositories&language=typescript" className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5 hover:bg-rose-500 active:bg-rose-900">TypeScript</Link>
<Link target="_blank" referrerPolicy="no-referrer" href="https://github.com/innocuous-symmetry?tab=repositories&q=react" className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5 hover:bg-rose-500 active:bg-rose-900">React</Link>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Next.js</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">tRPC</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">React Query</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">jQuery</p>
<Link target="_blank" referrerPolicy="no-referrer" href="https://github.com/innocuous-symmetry?tab=repositories&q=express" className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5 hover:bg-rose-500 active:bg-rose-900">Express</Link>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Node.js</p>
</div>
</details>
</article>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">CSS</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Tailwind</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Bootstrap</p>
<Link target="_blank" referrerPolicy="no-referrer" href="https://github.com/innocuous-symmetry/recipe-manager" className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5 hover:bg-rose-500 active:bg-rose-900">Sass</Link>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Material UI</p>
</div>
</details>
</article>
</div>
<div className="bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<h3 className="uppercase text-2xl text-rose-600">Experienced:</h3>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">Python</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<Link target="_blank" referrerPolicy="no-referrer" href="https://github.com/innocuous-symmetry/picosynth" className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5 hover:bg-rose-500 active:bg-rose-900">Micropython</Link>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Flask</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Flet</p>
</div>
</details>
</article>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">Database Operations</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<Link target="_blank" referrerPolicy="no-referrer" href="https://github.com/innocuous-symmetry/recipe-manager" className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5 hover:bg-rose-500 active:bg-rose-900">PostgreSQL</Link>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">MySQL</p>
<Link href="/about/work/dropper" className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5 hover:bg-rose-500 active:bg-rose-900">MongoDB</Link>
<Link href="/about/work/dropper" className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5 hover:bg-rose-500 active:bg-rose-900">Caching with Redis</Link>
</div>
</details>
</article>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">Tooling</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Docker</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Github Actions</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Git / Github</p>
</div>
</details>
</article>
</div>
<div className="bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<h3 className="uppercase text-2xl text-rose-600">Learning:</h3>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">Design Principles</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Domain Driven Design</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Microservices</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Readable Code</p>
</div>
</details>
</article>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">Cloud Development and Infrastructure</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">AWS SDK</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Proxmox</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Minio</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Linux</p>
</div>
</details>
</article>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">Server-side programming languages</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Ruby / Rails</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Golang</p>
</div>
</details>
</article>
<article className="mt-2">
<details className="group">
<summary className="flex items-center text-rose-300 uppercase tracking-wide text-lg mb-2 list-none">
<p className="mr-2">Natural Language Processing</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">NLPT</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Spacy</p>
</div>
</details>
</article>
</div>
<details className="group bg-slate-800 bg-opacity-40 shadow shadow-black p-3 my-4 rounded-lg">
<summary className="flex items-center uppercase text-2xl text-rose-600 list-none">
<p className="mr-2">Soft Skills:</p>
<RxChevronDown className="transition group-open:rotate-180" />
</summary>
<div className="opacity-0 group-open:opacity-100 transition-opacity duration-500 flex flex-wrap">
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Strong communicator</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Fast learner</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Self-driver</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Improviser</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Problem solver</p>
<p className="py-0.5 px-2 bg-rose-900 rounded-xl m-0.5">Design thinker</p>
</div>
</details>
</section>
);
export default Skills;

View File

@@ -0,0 +1,3 @@
export { default as Skills } from "./Skills";
export { default as Experience } from "./Experience";
export { default as Projects } from "./Projects";

32
components/SiteTree.tsx Normal file
View File

@@ -0,0 +1,32 @@
'use client'
import { usePathname } from 'next/navigation'
import Link from 'next/link'
import { v4 } from 'uuid'
export default function SiteTree() {
const pathname = usePathname()
const pathAsArray = pathname.split('/').filter(x => x !== '');
// return (
// <div className="flex flex-nowrap w-full">
// <Link href="/">
// <p className="mx-4">home</p>
// </Link>
// {pathAsArray.map((segment: string, idx: number) => {
// const path = pathAsArray.slice(0, idx + 1).join('/')
// return (
// <div key={v4()}>
// <p className="mr-4">/</p>
// <Link href={path}>
// <p className="mr-4">{segment}</p>
// </Link>
// </div>
// )
// })}
// </div>
// )
return <></>
}

82
components/logo/index.tsx Normal file
View File

@@ -0,0 +1,82 @@
'use client'
import { FC } from "react";
import useColorShift, { UseColorShiftReturnType, type ColorListType } from "./useColorShift";
import { useRouter } from "next/navigation";
export { default as useColorShift } from "./useColorShift";
const DEFAULT_SHIFT_INTERVAL = 3000;
interface LogoProps {
shiftInterval?: number,
customColorList?: ColorListType[],
disableShift?: boolean,
customHookInstance?: UseColorShiftReturnType
}
export const StackedLogo: FC<LogoProps> = ({ shiftInterval, customColorList, customHookInstance, disableShift = false }) => {
const hookProps = [
shiftInterval ?? DEFAULT_SHIFT_INTERVAL,
disableShift,
customColorList,
] as const;
const { firstColor, secondColor, thirdColor, shift } = useColorShift(...hookProps);
return (
<div id="venn-diagram-logo-container" className="flex w-full h-auto justify-center">
<div onMouseEnter={shift} onMouseOut={shift} className={`absolute flex flex-col z-40 items-center justify-center animate-logo-throw-left h-16 w-16 bg-opacity-75 ${firstColor} transition-colors duration-[5000ms] ease rounded-full`}>
<p className="text-6xl font-bold opacity-100">M</p>
</div>
<div onMouseEnter={shift} onMouseOut={shift} className={`absolute flex flex-col items-center justify-center animate-logo-throw-down h-16 w-16 bg-opacity-75 ${secondColor} transition-colors duration-[5000ms] ease rounded-full`}>
<p className="text-6xl font-bold opacity-100 z-50">C</p>
</div>
<div onMouseEnter={shift} onMouseOut={shift} className={`absolute flex flex-col items-center justify-center animate-logo-throw-right h-16 w-16 bg-opacity-75 ${thirdColor} transition-colors duration-[5000ms] ease rounded-full`}>
<p className="text-6xl font-bold opacity-100">D</p>
</div>
</div>
)
}
export const InlineLogo: FC<LogoProps> = ({ shiftInterval, customColorList, customHookInstance, disableShift = false }) => {
const router = useRouter();
const hookProps = [
shiftInterval ?? DEFAULT_SHIFT_INTERVAL,
disableShift,
customColorList,
] as const;
const { firstColor, secondColor, thirdColor, shift } = useColorShift(...hookProps);
if (customHookInstance) return (
<button onClick={() => router.push('/')} id="inline-logo-container" className="flex w-auto h-auto justify-center">
<div className={`flex flex-col items-center justify-center h-16 w-16 bg-opacity-75 ${customHookInstance.firstColor} transition-colors duration-[5000ms] rounded-full`}>
<p className="text-2xl font-bold opacity-100">M</p>
</div>
<div className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${customHookInstance.secondColor} transition-colors duration-[5000ms] rounded-full`}>
<p className="text-2xl font-bold opacity-100">C</p>
</div>
<div className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${customHookInstance.thirdColor} transition-colors duration-[5000ms] rounded-full`}>
<p className="text-2xl font-bold opacity-100">D</p>
</div>
</button>
)
return (
<button onClick={() => router.push('/')} id="inline-logo-container" className="flex w-auto h-auto justify-center">
<div className={`flex flex-col items-center justify-center h-16 w-16 bg-opacity-75 ${firstColor} transition-colors duration-[5000ms] rounded-full`}>
<p className="text-2xl font-bold opacity-100">M</p>
</div>
<div className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${secondColor} transition-colors duration-[5000ms] rounded-full`}>
<p className="text-2xl font-bold opacity-100">C</p>
</div>
<div className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${thirdColor} transition-colors duration-[5000ms] rounded-full`}>
<p className="text-2xl font-bold opacity-100">D</p>
</div>
</button>
)
}

View File

@@ -0,0 +1,63 @@
import { useEffect, useState } from "react";
import { DefaultColors } from "tailwindcss/types/generated/colors";
export type ColorListType = (`bg-${keyof DefaultColors}` | `bg-${keyof DefaultColors}-${string}` | "");
const colorList: ColorListType[] = [
"bg-purple-500",
"bg-purple-800",
"bg-sky-500",
"bg-sky-800",
"bg-blue-500",
"bg-pink-500",
"bg-pink-800",
];
export interface UseColorShiftReturnType {
firstColor: ColorListType
secondColor: ColorListType
thirdColor: ColorListType
shift: () => { firstColor: ColorListType, secondColor: ColorListType, thirdColor: ColorListType }
shiftInterval: number | undefined
}
const useColorShift = (shiftInterval?: number, disableShift = false, customColorList?: ColorListType[]): UseColorShiftReturnType => {
if (shiftInterval && shiftInterval <= 0) throw new Error("shiftInterval must be greater than 0")
const randomColor = () => (customColorList ?? colorList)[Math.floor(Math.random() * colorList.length | 0)];
const [circleColors, setCircleColors] = useState<{
firstColor: ColorListType | "",
secondColor: ColorListType | "",
thirdColor: ColorListType | "",
}>({
firstColor: colorList[0],
secondColor: colorList[1],
thirdColor: colorList[2],
})
function shift() {
const firstColor = randomColor();
const secondColor = randomColor();
const thirdColor = randomColor();
setCircleColors({ firstColor, secondColor, thirdColor });
return { firstColor, secondColor, thirdColor };
}
// perform initial mount of color changing pattern
useEffect(disableShift ? shift : (() => { return }), []);
// set this function to repeat
useEffect(() => {
const interval = setInterval(() => {
shift();
}, shiftInterval ?? 3000);
return () => clearInterval(interval);
}, [])
return { ...circleColors, shift, shiftInterval };
}
export default useColorShift

45
components/mdx/index.tsx Normal file
View File

@@ -0,0 +1,45 @@
import { v4 } from "uuid"
import { cabin } from "@/app/layout";
type ElementType<Key extends keyof JSX.IntrinsicElements> = React.FC<JSX.IntrinsicElements[Key]>
type FormattedTags = {
[Key in keyof JSX.IntrinsicElements]: ElementType<Key>
}
const H1TAG: ElementType<"h1"> = ({ children }) => { return (
<h1 key={v4()} className={`text-4xl text-red-500 ${cabin.className} tracking-wide`}>{children}</h1>
)}
const H2Tag: ElementType<"h2"> = ({ children }) => (
<h2 key={v4()}>{children}</h2>
)
const H3Tag: ElementType<"h3"> = ({ children }) => (
<h3 key={v4()}>{children}</h3>
)
const H4Tag: ElementType<"h4"> = ({ children }) => (
<h4 key={v4()}>{children}</h4>
)
const PTag: ElementType<"p"> = ({ children }) => (
<p key={v4()}>{children}</p>
)
const LiTag: ElementType<"li"> = ({ children }) => (
<li key={v4()}>{children}</li>
)
const BrTag: ElementType<"br"> = () => (
<br key={v4()} />
)
export default {
"h1": H1TAG,
"h2": H2Tag,
"h3": H3Tag,
"h4": H4Tag,
"p": PTag,
"li": LiTag,
"br": BrTag
} satisfies Partial<FormattedTags>

31
components/ui/Panel.tsx Normal file
View File

@@ -0,0 +1,31 @@
import { FC, ReactNode } from "react"
import { v4 } from "uuid"
import type { TailwindFraction, TailwindNumber } from "./types"
interface PanelProps {
children: ReactNode
width?: TailwindFraction | TailwindNumber | undefined
height?: TailwindFraction | TailwindNumber | undefined
}
const Panel: FC<PanelProps> = ({ children, width, height }) => {
const narrow = (input: TailwindNumber | TailwindFraction | undefined) => {
if (typeof input === 'number') return input.toString();
return input ?? "auto";
}
return (
<div
id={`ui-panel-${v4().slice(0, 6)}`}
className={`bg-inherit border-white border-2 border-opacity-50 border-double
drop-shadow-md shadow-white shadow-opacity-25
w-${narrow(width)} h-${narrow(height)}
rounded-lg p-3
`}
>
{children}
</div>
)
}
export default Panel

17
components/ui/types.ts Normal file
View File

@@ -0,0 +1,17 @@
const tailwindNumbers = [
0, 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 14, 16, 20, 24, 28,
32, 36, 40, 44, 48, 52, 56, 60, 64,
72, 80, 96
] as const;
const tailwindFractions = [
"1/2", "1/3", "2/3", "1/4", "2/4", "3/4",
"1/5", "2/5", "3/5", "4/5", "1/6", "2/6",
"3/6", "4/6", "5/6", "1/12", "2/12", "3/12",
"4/12", "5/12", "6/12", "7/12", "8/12", "9/12",
"10/12", "11/12"
] as const;
export type TailwindNumber = typeof tailwindNumbers[number];
export type TailwindFraction = typeof tailwindFractions[number];

View File

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta charset="UTF-8" />
<meta name="author" content="Mikayla Dobson" />
<meta name="description" content="Mikayla Dobson's Web Engineering Portfolio Site" />
<meta name="keywords" content="HTML, CSS, SCSS, React, Front End Developer, Front End Engineer, Full Stack Developer, Web Developer, JavaScript, TypeScript" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mikayla Dobson | Developer</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

12
mdx-components.tsx Normal file
View File

@@ -0,0 +1,12 @@
// https://github.com/vercel/next.js/blob/canary/examples/app-dir-mdx/mdx-components.tsx
import type { MDXComponents } from 'mdx/types'
// This file is required to use MDX in `app` directory.
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
// Allows customizing built-in components, e.g. to add styling.
// h1: ({ children }) => <h1 style={{ fontSize: "100px" }}>{children}</h1>,
...components,
}
}

26
next.config.js Normal file
View File

@@ -0,0 +1,26 @@
// more about configuring mdx
// https://nextjs.org/docs/pages/building-your-application/configuring/mdx
const withMDX = require('@next/mdx')({
extension: /\.mdx?$/,
options: {
// If you use remark-gfm, you'll need to use next.config.mjs
// as the package is ESM only
// https://github.com/remarkjs/remark-gfm#install
remarkPlugins: [require("remark-prism")],
rehypePlugins: [],
providerImportSource: "@mdx-js/react",
},
});
/** @type {import('next').NextConfig} */
const nextConfig = {
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
reactStrictMode: true,
experimental: {
// mdxRs: true,
// serverActions: true,
}
}
module.exports = withMDX(nextConfig);

2687
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,36 @@
{ {
"name": "about-mikayla-0722", "name": "mikayla-dobson-dev",
"version": "0.1.0",
"private": true, "private": true,
"version": "0.0.0",
"scripts": { "scripts": {
"dev": "vite", "dev": "next dev",
"build": "vite build", "build": "next build",
"preview": "vite preview" "start": "next start",
"lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.9.3", "@aws-sdk/client-s3": "^3.367.0",
"@emotion/styled": "^11.9.3", "@mdx-js/loader": "^2.3.0",
"@mui/icons-material": "^5.8.4", "@mdx-js/react": "^2.3.0",
"@mui/material": "^5.8.7", "@next/mdx": "^13.4.4",
"react": "^18.0.0", "@supabase/supabase-js": "^2.26.0",
"react-dom": "^18.0.0", "autoprefixer": "10.4.14",
"react-router-dom": "^6.3.0", "eslint": "^8.46.0",
"sass": "^1.53.0", "eslint-config-next": "^13.4.12",
"uuid": "^8.3.2" "next": "^13.4.12",
"postcss": "8.4.24",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.9.0",
"remark-prism": "^1.3.6",
"tailwindcss": "3.3.2",
"typescript": "5.0.4",
"uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.0", "@types/node": "20.2.5",
"@types/react-dom": "^18.0.0", "@types/react": "18.2.7",
"@vitejs/plugin-react": "^1.3.0", "@types/react-dom": "18.2.4",
"vite": "^2.9.9" "@types/uuid": "^9.0.1"
} }
} }

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 MiB

View File

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 272 KiB

1
public/next.svg Normal file
View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Binary file not shown.

1
public/vercel.svg Normal file
View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 629 B

View File

@@ -0,0 +1,19 @@
import supabaseClient from "../services/supabase";
export default class ProjectsActions {
static api = supabaseClient();
static async getProjects() {
const { data, error } = await this.api.from("projects").select("*");
if (error) throw error;
return data;
}
static async getProjectsById(id: string) {
const { data, error } = await this.api.from("projects").select("*").eq("id", id);
if (error) throw error;
return data;
}
}

View File

@@ -0,0 +1,21 @@
'use server';
import supabaseClient from "../services/supabase";
export default class WorkActions {
static api = supabaseClient();
static async getWork() {
const { data, error } = await this.api.from("work").select("*");
if (error) throw error;
return data;
}
static async getWorkById(id: string) {
const { data, error } = await this.api.from("work").select("*").eq("id", id);
if (error) throw error;
return data;
}
}

3
server/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export default async function main() {
return await Promise.resolve(12);
}

19
server/services/s3.ts Normal file
View File

@@ -0,0 +1,19 @@
import { S3Client } from "@aws-sdk/client-s3";
export default function createS3Client() {
if (typeof process.env.S3_ACCESS_KEY !== "string") {
throw new Error("S3_ACCESS_KEY is not defined");
}
if (typeof process.env.S3_SECRET !== "string") {
throw new Error("S3_SECRET is not defined");
}
return new S3Client({
region: "us-east-1",
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET,
},
});
}

View File

@@ -0,0 +1,13 @@
import { createClient } from "@supabase/supabase-js";
export default function supabaseClient() {
if (typeof process.env.SUPABASE_URL !== "string") {
throw new Error("SUPABASE_URL is not defined");
}
if (typeof process.env.SUPABASE_KEY !== "string") {
throw new Error("SUPABASE_KEY is not defined");
}
return createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY);
}

View File

@@ -1,33 +0,0 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Welcome from './pages/Welcome';
import AboutMe from './pages/AboutMe';
import Projects from './pages/Projects';
import Technologies from './pages/Technologies';
import Links from './pages/Links';
import CreativeWorks from './pages/CreativeWorks';
import Navbar from './components/Navbar';
import './sass/App.scss';
function App() {
return (
<div className="App">
<BrowserRouter>
<Navbar />
<main>
<Routes>
<Route path='/' element={<Welcome />} />
<Route path='about-me' element={<AboutMe />} />
<Route path='projects' element={<Projects />} />
<Route path='technologies' element={<Technologies />} />
<Route path='links' element={<Links />} />
<Route path='creative-works' element={<CreativeWorks />} />
</Routes>
</main>
</BrowserRouter>
</div>
);
}
export default App;

View File

@@ -1,15 +0,0 @@
import { useState } from "react"
export function AboutSection({ data }) {
const [visible, setVisible] = useState(false);
return (
<section className="about-me-section">
<div className="am-section-mini">
<h1>{data.title}</h1>
<button onClick={() => setVisible(!visible)}>{visible ? "Hide" : "Show"}</button>
</div>
{ visible && data.jsx }
</section>
)
}

View File

@@ -1,126 +0,0 @@
import { Card, Chip } from "@mui/material"
import { v4 } from 'uuid';
export const WhatIDo = {
title: "What I Do",
jsx: (
<div className="about-gallery">
<Card>
<h2 className="card-title">Create full stack web applications</h2>
<p>I have experience building web applications with and without back-end integrations.</p>
<p>I am comfortable conceptualizing and organizing complex structures, and as such, my projects tend to be
natural in their structure and easy to maintain.</p>
</Card>
<Card>
<h2 className="card-title">Collaborative software engineering</h2>
<p>I have consulted on small teams with:</p>
<div>
<p><a target="_blank" rel="noreferrer" href="https://dization.com">Dization</a>, a Pittsburgh-based company developing an enterprise resource planning solution for small businesses</p>
<p><a target="_blank" rel="noreferrer" href="https://www.metazu.studio/">Metazu Studio</a>, a Nashville-based startup connecting clients with services in video production, AR/VR, social media, photography, and web design.</p>
</div>
<p> My work on these projects deals with managing the complexities of full-stack web engineering, as well as delivering beautiful user experiences to end customers.</p>
</Card>
<Card>
<h2 className="card-title">Creative minded problem solver</h2>
<p>
My background as a musician, composer, producer, and artistic
collaborator provide me with a unique frame of reference for solving technical
problems and adapting to dynamic environments.
</p>
</Card>
<Card>
<h2 className="card-title">Database Operations and Management</h2>
<p>My projects have featured both relational and non-relational databases, in particular
PostgreSQL and MongoDB. I also have experience with various methods of connecting these
to front-end applications.</p>
</Card>
</div>
)
}
export const Education = {
title: "Education",
jsx: (
<div className="about-gallery">
<Card>
<h2 className="card-title">Bachelors of Arts, Music</h2>
<h3>Southern Methodist University, 2014 - 2017</h3>
<p><strong>Concentrations:</strong> Piano Performance, Music Composition, Music Theory</p>
<p><strong>Minor:</strong> French Language and Culture</p>
</Card>
<Card>
<h2 className="card-title">Self Directed Study, Web Engineering</h2>
<h3>2021 - present</h3>
<p>Codecademy Pro, Front End Engineer Career Path</p>
<p><strong>Concentrations:</strong> React, Redux, Express, PostgreSQL</p>
</Card>
</div>
)
}
const MetazuConcepts = [
"UI/UX design", "Pair Programming", "REST APIs", "Database management", "Consulting with clients"
]
const DizationConcepts = [
"Enterprise Resource Planning", "AWS", "System Design", "UI/UX Design", "Database management"
]
const MusicExperiences = [
"Avant Chamber Ballet", "Dallas Symphony Orchestra", "Boston University", "National Public Radio"
]
export const Employment = {
title: "Employment",
jsx: (
<div className="about-gallery">
<Card>
<h2 className="card-title">Full Stack Engineer, intern</h2>
<h3>Dization, Inc | Pittsburgh, PA (remote) <br/>Sept 2022 - present</h3>
<p><strong>Relevant Duties:</strong> contributing to the development of a full-stack enterprise resource planning application built using
the LAMP stack hosted on AWS.</p>
<div className="chip-section">
{ DizationConcepts.map(each => <Chip key={v4()} label={each} /> )}
</div>
</Card>
<Card>
<h2 className="card-title">Web Design Consultant</h2>
<h3>Metazu Studio | Nashville, TN <br/>Mar 2022 - present</h3>
<p><strong>Relevant Duties:</strong> collaboration on the design and production of responsive web apps, using skills including:</p>
<div className="chip-section">
{ MetazuConcepts.map(each => <Chip key={v4()} label={each} /> ) }
</div>
</Card>
<Card>
<h2 className="card-title">Singer-Songwriter, Composer</h2>
<h3>
Contract, Independent
<br/>
2010 - present
</h3>
<p>
Multi-instrumentalist, singer-songwriter, and producer, well-versed in a number of musical idioms
and music technologies.
</p>
<div className="chip-section">
{ MusicExperiences.map(each => <Chip key={v4()} label={each} /> ) }
</div>
</Card>
<Card>
<h2 className="card-title">Barista, Barista Trainer</h2>
<h3>Starbucks Coffee Co.<br/>2017 - 2021</h3>
<p>
<strong>Relevant Duties:</strong> collaboration, problem-solving, streamlined execution of a process,
attention to detail, adhering to standardized safety protocols, observing best practices, training new hires.
</p>
</Card>
</div>
)
}

View File

@@ -1,95 +0,0 @@
import { Button, Drawer, List, ListItem } from "@mui/material"
import { useEffect, useState } from "react"
import { useNavigate } from "react-router-dom";
import MenuIcon from '@mui/icons-material/Menu';
import '../sass/Navbar.scss';
import { v4 } from "uuid";
const navOptions = [
"Home",
"About Me",
"My Projects",
"My Technologies",
"Links",
"Creative Work"
]
export default function Navbar() {
const [open, setOpen] = useState(false);
const [selected, setSelected] = useState(null);
const navigate = useNavigate();
useEffect(() => {
setOpen(false);
switch (selected) {
case 0:
navigate('/');
break;
case 1:
navigate('/about-me');
break;
case 2:
navigate('/projects');
break;
case 3:
navigate('/technologies');
break;
case 4:
navigate('/links');
break;
case 5:
navigate('/creative-works');
break;
default:
navigate('/');
break;
}
}, [selected, navigate]);
return (
<header className="app-navbar">
<div className="navbar-left">
<a href="/" className="my-name">Mikayla Dobson</a>
<h2 className="mobile-hide">Web Engineer</h2>
</div>
<div className="navbar-right">
<Button onClick={() => setOpen(!open)}>
<MenuIcon />
</Button>
</div>
<Drawer
className="drawer"
anchor="right"
onClose={() => setOpen(false)}
open={open}
>
<List
component="nav"
sx={{height: '100vh', backgroundColor: '#e8eaf6'}}
>
{
navOptions.map(each => {
let idx = navOptions.indexOf(each);
return (
<ListItem button
className="drawer-list-item"
key={v4()}
selected={selected === idx}
onClick={() => setSelected(idx)}>
{each}
</ListItem>
)
})
}
</List>
</Drawer>
</header>
)
}

View File

@@ -1,136 +0,0 @@
import { Card } from "@mui/material";
import { v4 } from 'uuid';
export const projectsArray = [
{
name: "Express Spice Shop",
languages: ["TypeScript", "React", "PERN", "REST_API"],
inProgress: true,
jsx: (
<Card key={v4()}>
<h1><a target="_blank" rel="noreferrer" href="https://github.com/innocuous-symmetry/e-commerce">Express Spice Shop</a></h1>
<p>A sample, fully-featured e-commerce platform</p>
<p>Built in React with TypeScript, Node/Express, and PostgreSQL</p>
<p>Payment processing supported through Stripe (in progress)</p>
<p>API documentation with Swagger</p>
<p>IN PROGRESS</p>
</Card>
)
},
{
name: "Procedural Drones",
languages: ["JavaScript", "HTML/CSS"],
inProgress: true,
deployed: true,
jsx: (
<Card key={v4()}>
<h1>Procedural Music Generation</h1>
<p>An experimental space for building out procedural music generation using vanilla JavaScript</p>
<p>Uses Tone.js to interact with the Web Audio API</p>
<p>Features a plain HTML/CSS front end to interact with the program</p>
<div className="links">
<a target="_blank" rel="noreferrer" href="https://github.com/innocuous-symmetry/procedural-drones-01">GitHub Repo</a>
<a target="_blank" rel="noreferrer" href="https://innocuous-symmetry.github.io/procedural-drones-01/">Deployed version</a>
</div>
</Card>
)
},
{
name: "Reddit, but it's all cats",
languages: ["React", "Redux"],
inProgress: false,
deployed: true,
jsx: (
<Card key={v4()}>
<h1>Reddit, but it's all cats</h1>
<p>A read-only Reddit client -- this site fetches data from Reddit and displays a curated feed.</p>
<p>This was built on Reddit's JSON API, using React/Redux and CSS.</p>
<p>And yes, it's all cats.</p>
<div className="links">
<a target="_blank" rel="noreferrer" href="https://github.com/innocuous-symmetry/cat-reddit">GitHub Repo</a>
<a target="_blank" rel="noreferrer" href="https://reddit-but-all-cats.netlify.app/">Deployed version</a>
</div>
</Card>
)
},
{
name: "Personal Timestamp Generator",
languages: ["Python", "SQLite"],
inProgress: false,
jsx: (
<Card key={v4()}>
<h1>Personal Timestamp Generator</h1>
<p>A small-scale timestamp/productivity management tool for individual use and logging of
time, including compartmentalization by task and some aggregate functions based on queries.
</p>
<p>Command-line interface built on Python with a SQLite Database.</p>
<a href="https://github.com/innocuous-symmetry/timestamp_project" target="_blank" rel="noreferrer">View the repo here!</a>
</Card>
)
},
{
name: "Password Game",
languages: ["React", "MongoDB", "MERN", "React", "REST_API", "Socket.io", "Sass", "MaterialUI"],
inProgress: true,
jsx: (
<Card key={v4()}>
<h1>Password Game</h1>
<p>As part of a mentorship program hosted by Metazu Studio</p>
<p>Implemented using MongoDB, React, and Node/Express, styled with Material UI/SCSS.</p>
<p>In progress, building in collaboration with others at Metazu Studio.</p>
</Card>
)
},
{
name: "Splendor Clone",
languages: ["React", "TypeScript"],
inProgress: true,
jsx: (
<Card key={v4()}>
<h1><a target="_blank" rel="noreferrer" href="https://github.com/innocuous-symmetry/splendor-clone">Splendor</a> -- a clone of the board game by Marc André</h1>
<p>A clone of a popular card-based resource gathering game</p>
<p>Local multiplayer, with plans to build out online multiplayer and solo vs. CPU</p>
<p>In progress. Using React, Node/Express, and PostgreSQL.</p>
</Card>
)
},
{
name: "Recipin",
languages: ["React", "TypeScript", "PERN"],
inProgress: true,
jsx: (
<Card key={v4()}>
<h1><a target="_blank" rel="noreferrer" href="https://github.com/innocuous-symmetry/recipe-manager">Recipin</a> -- a home recipe management solution</h1>
<p>Manage a collection of your own commonly-used recipes</p>
<p>Features a full user-auth workflow and enables the sharing of recipes between users</p>
<p>Allows the user to build shopping lists from collections of their recipes</p>
<p>Built using full end-to-end TypeScript for the React front end and the Express server</p>
</Card>
)
},
{
name: "Photosorting with VGG16",
languages: ["Python", "TensorFlow", "PostgreSQL"],
inProgress: true,
jsx: (
<Card key={v4()}>
<h1>Photosorting with VGG16</h1>
<p>A tool written in Python for sorting photos based on the output of VGG16, the pre-trained image recognition model included with TensorFlow.</p>
<p>The user may also integrate the results of their photo sort into a PostgreSQL database.</p>
<p>The repository may be found <a target="_blank" rel="noreferrer" href="https://github.com/innocuous-symmetry/vgg16-image-recognition">here!</a></p>
</Card>
)
},
{
name: "About Mikayla",
languages: ["React", "Sass", "MaterialUI"],
inProgress: true,
jsx: (
<Card key={v4()}>
<h1>And, last but not least, the site you see here!</h1>
<p>This site is built using React, Material UI, and SCSS, and is hosted with Netlify.</p>
<a target="_blank" rel="noreferrer" href="https://github.com/innocuous-symmetry/MikaylaDobsonDev">View the site repo here!</a>
</Card>
)
}
]

View File

@@ -1,26 +0,0 @@
import { useState } from "react";
import { SubTopic } from "./SubTopic";
import { Card, Chip } from "@mui/material";
import { v4 } from "uuid";
export function MainTopic({ name, subtopics }) {
const [collapsed, setCollapsed] = useState(true);
return (
<Card className="main-topic" elevation={2}>
<h1>{name}</h1>
<div className="subtopic-container">
{
collapsed || subtopics.map(inner => {
return (
inner.subtopics ?
<SubTopic key={v4()} data={inner} />
: <Chip key={v4()} label={inner} />
)
})
}
</div>
<button onClick={() => setCollapsed(!collapsed)}>{collapsed ? "Show" : "Hide"}</button>
</Card>
)
}

View File

@@ -1,26 +0,0 @@
import { Card, Chip } from "@mui/material";
import { useEffect, useState } from "react";
import { v4 } from "uuid";
export function SubTopic({ data }) {
const [contents, setContents] = useState();
useEffect(() => {
setContents(() => {
if (data.subtopics.length) {
return data.subtopics.map(each => <Chip key={v4()} label={each} />);
} else {
return <p>In progress!</p>
}
})
}, []);
return (
<Card className="subtopic-card" elevation={5}>
<strong>{data.topic}</strong>
<div className="subtopics">
{contents}
</div>
</Card>
)
}

View File

@@ -1,104 +0,0 @@
export const techList = [
{
topic: "Programming Languages",
subtopics: [
{
topic: "JavaScript",
subtopics: [
"TypeScript",
"React",
"Redux",
"jQuery",
"NodeJS",
"Express",
"ToneJS",
"Jest"
]
},
{
topic: "Python",
subtopics: [
"Pandas",
"Beautiful Soup",
"TensorFlow, Keras",
"Flutter with Flet"
]
},
{
topic: "Java",
subtopics: [
"Spring Boot",
"Swing"
]
},
{
topic: "PHP",
subtopics: []
}
]
},
{
topic: "Front End Engineering",
subtopics: [
{
topic: "React",
subtopics: [
"Redux",
"React / TypeScript",
"React Native",
"React Testing Library",
"React Router"
]
},
{
topic: "Styling",
subtopics: [
"CSS",
"Sass",
"Bootstrap",
"Material UI",
"Responsive Design",
]
}
]
},
{
topic: "Back End Engineering",
subtopics: [
{
topic: "SQL",
subtopics: [
"PostgreSQL",
"MySQL",
"Apache Web Server",
"MongoDB",
"Supabase"
]
},
{
topic: "REST API",
subtopics: [
"Express.js",
"Express-Session",
"PHP / Apache / MySQL",
"Java / Spring Boot",
"API Documentation with Swagger",
"MVC Architecture"
]
}
]
},
{
topic: "Developer Tools",
subtopics: [
"Git",
"GitHub",
"Visual Studio Code",
"Vite",
"Netlify",
"Heroku",
"Figma",
]
}
]

View File

@@ -1,15 +0,0 @@
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
<stop stop-color="#41D1FF"/>
<stop offset="1" stop-color="#BD34FE"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA83"/>
<stop offset="0.0833333" stop-color="#FFDD35"/>
<stop offset="1" stop-color="#FFA800"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@@ -1,10 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.scss'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 889 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -1,14 +0,0 @@
import Card from '@mui/material/Card';
import { AboutSection } from '../components/AboutMe/AboutSection';
import { WhatIDo, Education, Employment } from '../components/AboutMe/SectionData';
import '../sass/pages/AboutMe.scss';
export default function AboutMe() {
return (
<div className="page about-me-page">
<AboutSection data={WhatIDo} />
<AboutSection data={Education} />
<AboutSection data={Employment} />
</div>
)
}

View File

@@ -1,49 +0,0 @@
import "../sass/pages/CreativeWorks.scss";
import { Card } from "@mui/material";
import modulars from "../media/modulars.jpeg";
import score from "../media/paper_score.jpeg";
import bandcamp from "../media/bandcamp-button-circle-line-black-128.png";
import soundcloud from "../media/soundcloud-icon.png";
export default function CreativeWorks() {
return (
<div className="page creative-works-page">
<h1>Creative works</h1>
<div className="cw-gallery">
<img src={score} alt="Original handwritten music notation" />
<img src={modulars} alt="Patch on a modular synthesizer" />
</div>
<section className="cw-information">
<p>I would be remiss not to take the opportunity to speak about how I feel my experience as a musician informs my work as a developer!</p>
<p>Working as a musician helped me to develop invaluable skills that provide me with a unique frame of reference in regards to my approach to technical problems.</p>
<p>See below some of the relevant applications of my musical experience:</p>
</section>
<section className="cw-experiences">
<p><strong>Modular synthesis</strong>, which heavily involves fundamentals of <strong>arithmetic and data flow</strong></p>
<p><strong>Orchestration</strong>; ensuring all instrumental parts are executable and comfortable to play within a given set of parameters</p>
<p><strong>Learning to play new instruments,</strong> and adapt my pre-existing knowledge to apply to new frames of reference</p>
<p><strong>Audio engineering</strong>, and the associated practices of learning/using <strong>music technology</strong></p>
<p>Proper <strong>interaction of components</strong> within a complex system</p>
<p>Managing <strong>proportional integrity</strong> in musical forms</p>
</section>
<section className="cw-examples">
<h2>If you're interested, you can find some samples of my music at the links below:</h2>
<div className="sources">
<Card>
<img src={soundcloud} alt="SoundCloud logo" />
<a href="https://soundcloud.com/mikaylamusic">My SoundCloud</a>
</Card>
<Card>
<img src={bandcamp} alt="Bandcamp logo" />
<a href="https://mikaylaclaire.bandcamp.com/releases">My Bandcamp</a>
</Card>
</div>
</section>
</div>
)
}

View File

@@ -1,23 +0,0 @@
import "../sass/pages/Links.scss";
import Card from '@mui/material/Card';
import github from "../media/GitHub-Mark-120px-plus.png";
import linkedin from "../media/LI-In-Bug.png";
export default function Links() {
return (
<div className="page links-page">
<h1>Check out the links below to get a better look at my work!</h1>
<div className="links-container">
<Card>
<img src={github} alt="GitHub logo" />
<a href="https://github.com/innocuous-symmetry">Github</a>
</Card>
<Card>
<img src={linkedin} alt="LinkedIn logo" />
<a href="https://www.linkedin.com/in/mikayla-dobson/">LinkedIn Profile</a>
</Card>
</div>
</div>
)
}

View File

@@ -1,113 +0,0 @@
import { useState, useEffect, useRef } from 'react';
import { projectsArray } from '../components/ProjectsArray';
import { Divider } from '@mui/material';
import '../sass/pages/Projects.scss';
export default function Projects() {
const [results, setResults] = useState();
const [search, setSearch] = useState(null);
const [text, setText] = useState();
const searchInput = useRef();
useEffect(() => {
let result = new Set();
if (!search) {
setResults(projectsArray.map(card => card.jsx));
return;
}
if (search && text) {
for (let entry of text) {
for (let field of entry.text) {
if (field.toLowerCase().includes(search)) {
result.add(projectsArray[entry.projectID]);
}
}
}
}
if (search && result.size == 0) {
setResults(
<p>No results found for search "{search}"</p>
)
} else {
setResults(Array.from(result).map(card => card.jsx));
}
}, [search]);
useEffect(() => {
function unpack(element) {
if (typeof element == "string") return element;
if (typeof element.props.children != "string") {
if (Array.isArray(element.props.children)) {
let composite = "";
for (let each of element.props.children) {
composite += unpack(each);
}
return composite;
}
return unpack(element.props.children);
}
return element.props.children;
}
let projectText = [];
let i = 0;
while (i < projectsArray.length) {
let project = {
projectID: i,
text: []
}
let newText;
for (let each of projectsArray[i].jsx.props.children) {
newText = unpack(each);
project.text.push(newText);
}
projectText.push(project);
i++;
}
setText(projectText);
setResults(projectsArray.map(card => card.jsx));
}, [])
const handleReset = () => {
setSearch(null);
searchInput.current.value = null;
}
return (
<div className="page projects-page">
<h1 className="projects-section-header mobile-hide">Check out these projects from my portfolio!</h1>
<h1 className="projects-section-header mobile-show">A selection of my projects:</h1>
<section className="filter-panel">
<h2>Filter by:</h2>
<div className="filter-controls">
<input
ref={searchInput} type="text"
onChange={(e) => setSearch(e.target.value.toLowerCase())}
placeholder="Enter a search term">
</input>
<button onClick={handleReset}>Reset</button>
</div>
</section>
<Divider orientation="horizontal" className="divider" />
<div className="project-cards">
{results}
</div>
</div>
)
}

View File

@@ -1,23 +0,0 @@
import '../sass/pages/Technologies.scss';
import { MainTopic } from '../components/Technologies/MainTopic';
import { techList } from '../components/Technologies/techList';
import { v4 } from 'uuid';
export default function Technologies() {
return (
<div className="page technologies-page">
<p className="tech-title">These are some of my most frequently used skills and technologies:</p>
<div className="topics">
{techList.map(item => {
return (
<MainTopic
key={v4()}
name={item.topic}
subtopics={item.subtopics}
/>
);
})}
</div>
</div>
)
}

View File

@@ -1,35 +0,0 @@
import { Avatar, Card, Divider } from '@mui/material';
import profile from '../media/profile.jpeg';
import '../sass/pages/Welcome.scss';
export default function Welcome() {
return (
<div className="page welcome-page">
<Card id="header-card" elevation={5}>
<Avatar alt="Mikayla Dobson" src={profile} sx={{width: 110, height: 110}} />
<h3>Hi, my name is Mikayla Dobson!</h3>
<h4>Thanks for taking the time to learn more about me!</h4>
</Card>
<Divider orientation="horizontal" className="divider" />
<Card id="welcome-info">
<p>
I am a software engineer based in Nashville, Tennessee. I build performant
web applications, seek out simple solutions to complex problems, and emphasize
elegance of design.
</p>
<p>
Some of my most frequently used technologies include React, Redux, Node.js,
Express, TypeScript, Vite, PostgreSQL, MongoDB, and Sass, among others.
</p>
</Card>
<Divider orientation="horizontal" className="divider" />
<footer>
<p>&copy; Mikayla Dobson 2022</p>
</footer>
</div>
);
}

View File

@@ -1,30 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap');
@import "helper/queries";
@import "helper/variables";
@import "components/Page";
.App {
.mobile-show {
display: none;
}
button {
border: transparent;
border-radius: 6px;
}
main {
text-align: center;
position: relative;
top: 4.5rem;
.page {
@extend %page;
}
}
@extend %tablet-queries;
@extend %mobile-queries;
}

View File

@@ -1,83 +0,0 @@
@import "helper/variables";
@import "components/Button";
.app-navbar {
display: flex;
position: fixed;
top: 0;
background-color: $darkGreen;
color: $indigoWhite;
font-family: 'Open Sans', sans-serif;
border-bottom: 1px solid black;
width: 100vw;
height: 4.5rem;
z-index: 9;
button {
@extend %button-style;
color: $indigoWhite;
border-color: transparent;
border-radius: 0;
position: absolute;
right: 0;
height: 100%;
&:hover {
background-color: $mintGreen;
}
}
.navbar-left {
display: flex;
flex-direction: row;
width: 75%;
justify-content: flex-start;
align-items: baseline;
}
.navbar-right {
display: flex;
flex-direction: row;
justify-content: flex-end;
padding-right: 1rem;
width: 25%;
}
// top right corner, Mikayla Dobson text
a {
transition: color 150ms ease;
font-weight: 900;
font-size: 2rem;
padding-left: 1rem;
border-right: 1px solid black;
padding-right: 1rem;
text-decoration: none;
&:visited {
color: inherit;
}
&:hover {
color: $mintGreen;
transition: color 150ms ease;
}
}
h2 {
font-weight: 400;
position: relative;
padding-left: 1rem;
}
// styles for open sidebar
.drawer {
.MuiButtonBase-root {
background-color: red;
}
.drawer-list {
height: 100vh;
background-color: red;
}
.drawer-list-item {
background-color: orange;
}
}
}

View File

@@ -1,15 +0,0 @@
@import "../helper/variables";
%button-style {
border: transparent;
background-color: inherit;
color: $indigoOne;
padding: 10px 1rem;
&:hover {
background-color: $lightIndigo;
color: $indigoOne;
}
&:active {
background-color: $indigoWhite;
}
}

View File

@@ -1,5 +0,0 @@
%divider {
width: 80%;
color: #000;
border-width: 2px;
}

View File

@@ -1,5 +0,0 @@
@import "../helper/animations";
%fadeAll {
animation: 0.5s fade-in 0.6s linear forwards;
}

View File

@@ -1,11 +0,0 @@
@import "../helper/variables";
@import "../components/FadeAll";
%page {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
width: 100vw;
background-color: #fff;
}

View File

@@ -1,8 +0,0 @@
@keyframes fade-in {
from {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@@ -1,9 +0,0 @@
@import "../helper/animations";
@mixin step-entry($len) {
@for $i from 1 through $len {
&:nth-child(#{$i}) {
animation: 0.5s fade-in #{$i * 0.6 - 0.6}s linear forwards;
}
}
}

View File

@@ -1,236 +0,0 @@
@import "./mixins";
@import "./variables";
%tablet-queries {
@media only screen and (max-width: 1050px) {
.welcome-page {
#header-card {
padding: 0.7rem;
h3 {
font-size: 1.2rem;
}
h4 {
font-size: 1rem;
}
}
}
.about-me-page {
height: auto;
.about-me-section {
h1 {
font-size: 2.25rem;
}
button {
padding: 8px;
}
}
.about-gallery {
margin-top: 0;
width: 90vw;
height: 100%;
flex-flow: column nowrap;
align-items: center;
.MuiPaper-root {
width: 55%;
height: auto;
overflow-y: scroll;
}
}
}
.projects-page {
.filter-panel {
margin-bottom: 2rem;
width: 65%;
}
}
.technologies-page {
.main-topic {
width: 70vw;
}
}
.creative-works-page {
> * {
width: 100%;
}
.cw-gallery img {
max-width: 45%;
}
.cw-information, .cw-experiences {
width: 90%;
}
.cw-examples {
.examples-left {
width: 40vw;
}
.examples-right {
margin-right: 1rem;
h2 {
padding: 1rem;
}
width: 50vw;
}
}
}
}
}
%mobile-queries {
@media only screen and (max-width: 600px) {
// universal tags for handling mobile-sensitive elements
.mobile-hide {
display: none;
}
.app-navbar {
.navbar-left {
width: 80vw;
align-items: center;
.my-name {
border-right: none;
}
}
.navbar-right {
width: 20vw;
align-items: center;
button {
padding: 0;
height: auto;
}
}
a, h2 {
color: inherit;
font-weight: 600;
font-size: 1.25rem;
}
}
.welcome-page {
#header-card {
animation: none;
width: 75vw;
}
#welcome-info {
animation: none;
}
}
.about-me-page {
.about-me-section {
.am-section-mini {
background-color: $darkLilac;
position: sticky;
top: 4.5rem;
width: 88vw;
z-index: 8;
h1 {
font-size: 1.35rem;
}
button {
padding: 4px;
height: 1.5rem;
}
}
.about-gallery {
.MuiPaper-root {
width: 85%;
height: auto;
margin-bottom: 12px;
.card-title {
font-size: 1.2rem;
}
h3 {
font-size: 1rem;
}
}
}
}
}
.projects-page {
.mobile-show {
display: block;
font-size: 1.35rem;
}
.filter-panel {
position: sticky;
top: 5.5rem;
height: 4rem;
margin: 1.5rem;
h2 {
margin-top: 0;
font-size: 1.15rem;
}
.filter-controls input, button {
margin: 0 8px;
border-radius: 8px;
}
}
.project-cards {
margin-top: 1.5rem;
.MuiPaper-root {
padding: 8px;
h1 {
font-size: 1.5rem;
}
}
}
}
.technologies-page {
.tech-title {
width: 95vw;
}
.main-topic {
width: 85vw;
h1 {
font-size: 1.2rem;
}
button {
padding: 6px;
}
}
}
.links-page {
margin-top: 1rem;
a {
text-decoration: none;
}
h1 {
font-size: 1.35rem;
width: 85%;
margin-top: 1rem;
}
justify-content: flex-start;
height: 20rem;
}
.creative-works-page {
.cw-gallery {
flex-flow: column nowrap;
img {
max-width: 65%;
margin: 1rem 0;
}
}
.cw-information {
width: 95vw;
margin: 0;
text-align: left;
}
}
}
}

View File

@@ -1,12 +0,0 @@
$indigoOne: #301755;
$indigoWhite: #e8eaf6;
$lightIndigo: #8191ec;
$lilac: #e2e0fe;
$lilacWhite: #eae4f4;
$darkLilac: #caa5dd;
$darkBlueGreen: #1a6f6d;
$darkGreen: #264935;
$mintGreen: #bef9ca;

View File

@@ -1,102 +0,0 @@
@import "../helper/variables";
@import "../helper/mixins";
.about-me-page {
.about-me-section {
display: flex;
flex-flow: column nowrap;
align-items: center;
background-color: $darkLilac;
border: 1px solid $indigoOne;
border-radius: 12px;
margin: 1rem;
padding: 8px 2rem;
width: 75vw;
border: transparent;
opacity: 0;
transition: 0.5s transform ease;
@include step-entry(4);
.am-section-mini {
display: inline-flex;
flex-flow: row wrap;
align-items: center;
justify-content: space-between;
width: 100%;
h1 {
font-size: 2.5rem;
}
> * {
margin: 8px;
}
button {
display: inline-flex;
align-items: center;
height: 2rem;
padding: 1rem;
}
}
.about-gallery {
display: flex;
flex-flow: row wrap;
height: auto;
width: 95%;
justify-content: space-around;
margin: 2rem;
.MuiCard-root {
.card-title {
text-transform: uppercase;
}
display: flex;
flex-direction: column;
background-color: $indigoOne;
color: $indigoWhite;
text-align: center;
align-items: center;
opacity: 0;
transition: 0.5s transform ease;
width: 40%;
height: 400px;
border-radius: 12px;
padding: 1rem;
margin: 0.5rem;
margin-bottom: 3rem;
overflow-y: scroll;
@include step-entry(4);
.MuiChip-root {
background-color: $indigoWhite;
max-width: 75%;
margin: 4px;
}
}
}
}
h1 {
color: $indigoOne;
font-weight: 800;
font-size: 4rem;
}
a {
color: white;
border-radius: 12px;
transition: color 0.3s linear;
&:hover {
color: white;
transition: color 0.3s linear;
}
}
}

View File

@@ -1,88 +0,0 @@
@import "../helper/variables";
.creative-works-page {
display: flex;
flex-direction: column;
section {
margin-bottom: 3rem;
p {
display: inline-block;
background-color: white;
margin: 4px 8px;
padding: 1rem;
border-radius: 12px;
list-style: none;
}
h2 {
font-size: 1.2rem;
}
}
.cw-gallery {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 30rem;
margin-bottom: 3rem;
img {
display: inline;
height: 90%;
width: auto;
object-fit: cover;
margin: 0 1rem;
}
}
.cw-information {
display: flex;
flex-flow: row wrap;
justify-content: flex-end;
text-align: right;
margin: 0 2rem 3rem 0;
p {
background-color: rgb(200, 219, 225);
}
}
.cw-experiences {
display: flex;
flex-flow: row wrap;
text-align: left;
p {
background-color: $indigoWhite;
}
}
.cw-examples {
display: flex;
min-width: 100%;
flex-flow: column nowrap;
align-items: center;
.sources {
display: inline-flex;
flex-flow: row nowrap;
justify-content: space-evenly;
width: 100%;
> * {
display: inline-flex;
flex-flow: column nowrap;
width: 35%;
height: 15rem;
align-items: center;
justify-content: space-evenly;
background-color: $indigoWhite;
margin-top: 1rem;
img {
width: 100px;
height: 100px;
margin-bottom: 1rem;
}
a {
color: $indigoOne;
}
}
}
}
}

View File

@@ -1,53 +0,0 @@
@import "../helper/variables";
@import "../helper/mixins";
.links-page {
background-color: $lilac;
justify-content: center;
.links-container {
display: flex;
flex-flow: row wrap;
.MuiCard-root {
@include step-entry(2);
opacity: 0;
transition: 0.5s transform ease;
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
height: 8rem;
width: 20vw;
padding: 2rem;
margin: 1rem;
background-color: $lilacWhite;
a {
color: $indigoOne;
text-transform: uppercase;
text-underline-offset: 5px;
&:hover {
color: $darkGreen;
}
&:active {
color: $indigoOne;
}
&:visited {
color: inherit;
}
}
img {
width: 4rem;
height: auto;
}
}
}
}

View File

@@ -1,72 +0,0 @@
@import "../components/Divider";
@import "../helper/variables";
.projects-page {
.divider {
@extend %divider;
}
.filter-panel {
display: flex;
position: static;
flex-direction: column;
align-items: center;
background-color: $darkGreen;
color: $indigoWhite;
padding: 1rem;
margin-bottom: 2rem;
border-radius: 12px;
width: 40vw;
.filter-controls {
display: flex;
justify-content: center;
width: 80%;
> * {
margin: 1rem;
}
input {
padding: 6px;
border-radius: 4px;
}
}
}
.project-cards {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 2rem;
a {
color: $indigoWhite;
&:hover {
color: lightblue;
}
}
> * {
display: flex;
flex-direction: column;
align-items: center;
background-color: $indigoOne;
color: $indigoWhite;
width: 80vw;
height: auto;
margin-bottom: 2rem;
border-radius: 12px;
.links {
display: flex;
width: 75%;
align-items: baseline;
justify-content: space-around;
}
a:last-child {
margin-bottom: 1rem;
}
}
}
}

View File

@@ -1,65 +0,0 @@
@import "../helper/variables";
@import "../helper/animations";
@import "../helper/mixins";
@import "../components/Button";
.technologies-page {
.tech-title {
font-size: 1.4rem;
font-weight: bold;
}
.main-topic {
display: flex;
flex-direction: column;
align-items: center;
opacity: 0;
transition: 0.5s transform ease;
@include step-entry(4);
background-color: $indigoOne;
color: $indigoWhite;
width: 50vw;
border-radius: 16px;
margin: 1rem 0;
padding: 1rem;
button {
@extend %button-style;
background-color: $indigoWhite;
}
}
.subtopic-container {
display: flex;
flex-flow: row wrap;
justify-content: center;
.subtopic-card {
width: 75%;
margin: 1rem;
padding: 1rem;
.subtopics {
display: flex;
flex-flow: row wrap;
justify-content: center;
padding: 1rem;
& > * {
padding: 0.5rem;
margin: 0.5rem;
}
}
&:last-child {
margin-bottom: 2rem;
}
}
.MuiChip-root {
margin: 5px 4px 1rem;
background-color: $indigoWhite;
}
}
}

View File

@@ -1,52 +0,0 @@
@import "../helper/animations";
@import "../helper/variables";
@import "../components/Divider";
.welcome-page {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
overflow-x: hidden;
#header-card {
display: flex;
flex-direction: column;
align-items: center;
width: 35vw;
height: 18rem;
padding: 1rem;
margin: 2rem;
font-size: 1.2rem;
border-radius: 12px;
background-color: $indigoOne;
color: $lilac;
// animation: sideToSide 10s infinite;
}
#welcome-info {
margin: 2rem 0;
width: 75%;
font-size: 1rem;
font-weight: 500;
padding: 0.6rem;
// animation: sideToSide 15s infinite;
background-color: $indigoOne;
color: $lilac;
}
.divider {
@extend %divider;
}
footer {
width: 80%;
display: block;
text-align: right;
}
}

64
tailwind.config.js Normal file
View File

@@ -0,0 +1,64 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
safelist: [
{
pattern: /from-.*/,
},
{
pattern: /to-.*/,
}
],
theme: {
extend: {
colors: {
'darkPlum': '#1e0631',
},
transitionTimingFunction: {
'quick-start': 'cubic-bezier(.17,.67,0,.89)',
},
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
animation: {
'logo-throw-left': 'logoThrowLeft 1s ease forwards',
'logo-throw-right': 'logoThrowRight 1s ease forwards',
'logo-throw-down': 'logoThrowDown 1s ease forwards',
'fade-in': 'fadeIn 1s ease forwards',
'text-gradient': 'textGradient 15s ease infinite'
},
keyframes: {
fadeIn: {
'0%': { opacity: 0 },
'100%': { opacity: 1 }
},
logoThrowLeft: {
'0%': { transform: 'translateX(0)'},
'100%': { transform: 'translateX(-56px)' }
},
logoThrowRight: {
'0%': { transform: 'translateX(0)'},
'100%': { transform: 'translateX(56px)' }
},
logoThrowDown: {
'0%': { transform: 'translateY(0)'},
'100%': { transform: 'translateY(80px)' }
},
textGradient: {
'0%': { opacity: 0},
'12%': { opacity: 100 },
'88%': { opacity: 98 },
'100%': { opacity: 0 }
}
}
},
},
plugins: [],
}

28
tsconfig.json Normal file
View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -1,7 +0,0 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()]
})