diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..02b5d6faa Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 6b1038f3f..3932fecb3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,12 @@ lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + # Runtime data pids *.pid diff --git a/README.md b/README.md index ed53598d9..ba252c01c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-

andrewnelson.net | portfolio

+

riptidestar.com | portfolio

Experimental Vercel @@ -11,19 +11,6 @@ -## Updates -#### Dev Update — July 6th, 2023 🥰 -We have relocated to a beautiful home in the suburbs of Langely, BC. There has been a LOT of work unpacking, organizing, and landscaping to get this home perfect for our two toddlers! I am hoping to resume development of the theme in the next couple months! - -On a really interesting note, Webflow has launched a React component export tool. I am considering moving all component design and management over to Webflow for easier tooling for marketing teams and visual designers! -#### Dev Update — Feb 13th, 2023 [Updated: March 05th, 2023] -Jest has been setup, but no progress on building tests has been made. Life is busy atm! -#### Dev Update — Feb 10th, 2023 -I am currently taking a short break in development of this theme. I am in the final phases of a job search, and getting ready to move into a bigger place to give some much needed room to my growing family! I am hoping to continue development before the end of next quarter! - -## This website is still being developed! 🥳 -The porftofolio application has currently hit the Beta phase and is ready to be forked if you are familiar with React and Next. The next release cycle will focus on a better DX—moving from local JSON to MongoDB, setting up Sanity for GUI page editing, Unit Testing with Jest, adding TypeScript, etc. - #### Next & App Architecure - [ ] Next.js v13 : Waiting for /app/ folder to leave beta - [ ] Sanity.io @@ -51,8 +38,18 @@ The porftofolio application has currently hit the Beta phase and is ready to be Fork, Install, Editing, and Deploy instructions coming soon(ish)! -This is my first open-source contribution, and it was also a great oppoortunity for me to learn! - It is my hope that by sharing this project it can give others a chance to enjoy learning Nextjs (and have as much fun as I did), and hopefully brings value to people as a portfolio and networking tool! +## Contact Form Email Setup (Resend) + +To enable the contact form, set the following environment variable in your `.env.local` file: + +``` +RESEND_API_KEY=your_resend_api_key_here +``` + +- Get your API key from the [Resend dashboard](https://resend.com/). +- You must verify your sending domain in Resend to send from your own domain. +- The contact form will send submissions to kyle100@upenn.edu. + diff --git a/components/blocks/projects/featured.jsx b/components/blocks/projects/featured.jsx index 5b3b1d0a5..bea85efa1 100644 --- a/components/blocks/projects/featured.jsx +++ b/components/blocks/projects/featured.jsx @@ -1,4 +1,5 @@ import Image from 'next/image' +import Link from 'next/link' import { useEffect } from 'react' import { m, useAnimation } from "framer-motion" @@ -26,48 +27,50 @@ export default function FeaturedProject({ content }, index) { }, [ controls, inView ] ) return ( - - -
-
-
-

{project}

{repo} + + + +
+
+
+

{project}

{repo} +
+
+

{descriptionTitle} {description}

+
+
+ +
+ + +
-
-

{descriptionTitle} {description}

-
-
- -
- - -
-
-
- - { images.map( ({key, url, hover, h, w }, index) => { - hover = ( hover === 'left' ) ? hoverLeft : hoverRight - return ( - - - x +
+ + { images.map( ({key, url, hover, h, w }, index) => { + hover = ( hover === 'left' ) ? hoverLeft : hoverRight + return ( + + + x + - - )} - ) } - -
- + )} + ) } +
+
+ + ) } @@ -136,4 +139,3 @@ const hoverRight = { x: 20 } } - diff --git a/components/layout/footer.jsx b/components/layout/footer.jsx index e3ab895e6..ed669fcaf 100644 --- a/components/layout/footer.jsx +++ b/components/layout/footer.jsx @@ -71,7 +71,7 @@ export default function Footer() { -
+ {/*
{settings.portfolio.forkthis}
    @@ -83,7 +83,7 @@ export default function Footer() {
-
+
*/} diff --git a/components/redesign/Card.jsx b/components/redesign/Card.jsx new file mode 100644 index 000000000..337bf3887 --- /dev/null +++ b/components/redesign/Card.jsx @@ -0,0 +1,44 @@ +import { motion } from 'framer-motion'; +import styles from './Card.module.scss'; + +const cardVariants = { + hidden: { + opacity: 0, + y: 30 + }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.6, + ease: [0.25, 0.46, 0.45, 0.94], + }, + }, +}; + +export default function Card({ + id, + title, + children, + className = '', + delay = 0 +}) { + return ( + + {title && ( +

{title}

+ )} +
+ {children} +
+
+ ); +} diff --git a/components/redesign/Card.module.scss b/components/redesign/Card.module.scss new file mode 100644 index 000000000..52a37c1ee --- /dev/null +++ b/components/redesign/Card.module.scss @@ -0,0 +1,57 @@ +.card { + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + padding: var(--space-8); + margin-bottom: var(--space-6); + transition: border-color var(--transition-base); + + &:hover { + border-color: var(--border-hover); + } + + @media (max-width: 640px) { + padding: var(--space-6); + margin-bottom: var(--space-4); + } +} + +.title { + font-size: var(--text-2xl); + font-weight: 400; + margin-bottom: var(--space-6); + padding-bottom: var(--space-4); + border-bottom: 1px solid var(--border-color); + font-family: var(--font-sans); + + @media (max-width: 640px) { + font-size: var(--text-xl); + margin-bottom: var(--space-4); + padding-bottom: var(--space-3); + } +} + +.content { + p { + color: var(--text-secondary); + line-height: 1.7; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: var(--accent); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + strong { + color: var(--text-primary); + font-weight: 600; + } +} diff --git a/components/redesign/DotMatrix.jsx b/components/redesign/DotMatrix.jsx new file mode 100644 index 000000000..356002189 --- /dev/null +++ b/components/redesign/DotMatrix.jsx @@ -0,0 +1,299 @@ +import { useState, useEffect, useCallback, useMemo } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import styles from './DotMatrix.module.scss'; + +// Grid configuration +const GRID_SIZE = 15; +const DOT_COUNT = GRID_SIZE * GRID_SIZE; + +// Icon definitions - each icon is a set of dot indices that should be "active" +// Grid is 15x15 (0-224), calculated as row * 15 + col +const generateIconDots = (pattern) => { + const dots = new Set(); + pattern.forEach(([row, col]) => { + if (row >= 0 && row < GRID_SIZE && col >= 0 && col < GRID_SIZE) { + dots.add(row * GRID_SIZE + col); + } + }); + return dots; +}; + +// Rocket League car icon - stylized car silhouette +const rocketLeagueCar = generateIconDots([ + // Car body + [7, 3], [7, 4], [7, 5], [7, 6], [7, 7], [7, 8], [7, 9], [7, 10], [7, 11], + [8, 2], [8, 3], [8, 4], [8, 5], [8, 6], [8, 7], [8, 8], [8, 9], [8, 10], [8, 11], [8, 12], + [9, 2], [9, 3], [9, 4], [9, 5], [9, 6], [9, 7], [9, 8], [9, 9], [9, 10], [9, 11], [9, 12], + // Cabin/top + [6, 5], [6, 6], [6, 7], [6, 8], [6, 9], + [5, 6], [5, 7], [5, 8], + // Wheels + [10, 3], [10, 4], [11, 3], [11, 4], + [10, 10], [10, 11], [11, 10], [11, 11], + // Boost flames + [8, 13], [9, 13], [8, 14], [7, 13], +]); + +// Neural network / AI brain icon +const neuralNetwork = generateIconDots([ + // Input layer (left) + [3, 2], [5, 2], [7, 2], [9, 2], [11, 2], + // Hidden layer 1 + [4, 5], [6, 5], [8, 5], [10, 5], + // Hidden layer 2 + [5, 8], [7, 8], [9, 8], + // Output layer + [6, 11], [8, 11], + // Connections (dots along the paths) + [3, 3], [4, 4], [5, 3], [6, 4], [7, 3], [8, 4], [9, 3], [10, 4], [11, 3], + [4, 6], [5, 7], [6, 6], [7, 7], [8, 6], [9, 7], [10, 6], + [5, 9], [6, 10], [7, 9], [8, 10], [9, 9], + // Brain outline hint + [2, 6], [2, 7], [2, 8], [12, 6], [12, 7], [12, 8], +]); + +// Microphone / Music note for a cappella +const microphone = generateIconDots([ + // Mic head (circle) + [3, 6], [3, 7], [3, 8], + [4, 5], [4, 9], + [5, 5], [5, 9], + [6, 5], [6, 9], + [7, 6], [7, 7], [7, 8], + // Mic body + [8, 7], [9, 7], [10, 7], + // Stand base + [11, 5], [11, 6], [11, 7], [11, 8], [11, 9], + [12, 4], [12, 5], [12, 6], [12, 7], [12, 8], [12, 9], [12, 10], + // Sound waves + [4, 3], [5, 2], [6, 3], + [4, 11], [5, 12], [6, 11], + [3, 4], [3, 10], +]); + +// Globe / Travel / Wanderlust icon +const wanderlust = generateIconDots([ + // Globe circle + [2, 7], + [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], + [4, 4], [4, 10], + [5, 3], [5, 11], + [6, 3], [6, 11], + [7, 3], [7, 11], + [8, 3], [8, 11], + [9, 3], [9, 11], + [10, 4], [10, 10], + [11, 5], [11, 6], [11, 7], [11, 8], [11, 9], + [12, 7], + // Longitude line + [4, 7], [5, 7], [6, 7], [7, 7], [8, 7], [9, 7], [10, 7], + // Latitude lines + [5, 5], [5, 6], [5, 8], [5, 9], + [7, 4], [7, 5], [7, 6], [7, 8], [7, 9], [7, 10], + [9, 5], [9, 6], [9, 8], [9, 9], + // Plane + [4, 12], [5, 13], [6, 12], [5, 11], [5, 12], +]); + +// Lightbulb / Innovation icon +const lightbulb = generateIconDots([ + // Bulb top + [2, 7], + [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], + [4, 4], [4, 10], + [5, 3], [5, 11], + [6, 3], [6, 11], + [7, 4], [7, 10], + [8, 5], [8, 9], + // Neck + [9, 6], [9, 7], [9, 8], + // Base + [10, 5], [10, 6], [10, 7], [10, 8], [10, 9], + [11, 6], [11, 7], [11, 8], + [12, 6], [12, 7], [12, 8], + // Filament/rays + [4, 6], [4, 7], [4, 8], + [5, 7], + // Rays emanating + [1, 7], + [2, 4], [2, 10], + [4, 2], [4, 12], +]); + +// Rocket icon for entrepreneurship +const rocket = generateIconDots([ + // Nose cone + [2, 7], + [3, 6], [3, 7], [3, 8], + // Body + [4, 5], [4, 6], [4, 7], [4, 8], [4, 9], + [5, 5], [5, 6], [5, 7], [5, 8], [5, 9], + [6, 5], [6, 6], [6, 7], [6, 8], [6, 9], + [7, 5], [7, 6], [7, 7], [7, 8], [7, 9], + [8, 5], [8, 6], [8, 7], [8, 8], [8, 9], + // Fins + [7, 3], [8, 3], [9, 4], + [7, 11], [8, 11], [9, 10], + // Engine + [9, 6], [9, 7], [9, 8], + // Flames + [10, 7], + [11, 6], [11, 7], [11, 8], + [12, 5], [12, 7], [12, 9], + [13, 6], [13, 8], +]); + +// Code brackets icon - for developer +const codeBrackets = generateIconDots([ + // Left bracket < + [4, 4], [5, 3], [6, 2], [7, 2], [8, 2], [9, 3], [10, 4], + // Slash / + [5, 8], [6, 7], [7, 7], [8, 7], [9, 6], + // Right bracket > + [4, 10], [5, 11], [6, 12], [7, 12], [8, 12], [9, 11], [10, 10], + // Dots for decoration + [3, 6], [3, 7], [3, 8], + [11, 6], [11, 7], [11, 8], +]); + +// Heart icon - for passion/creativity +const heart = generateIconDots([ + // Top curves + [4, 4], [4, 5], [4, 9], [4, 10], + [3, 3], [3, 4], [3, 5], [3, 6], [3, 8], [3, 9], [3, 10], [3, 11], + [4, 2], [4, 3], [4, 6], [4, 8], [4, 11], [4, 12], + [5, 2], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [5, 12], + // Middle + [6, 3], [6, 4], [6, 5], [6, 6], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], + [7, 4], [7, 5], [7, 6], [7, 7], [7, 8], [7, 9], [7, 10], + [8, 5], [8, 6], [8, 7], [8, 8], [8, 9], + // Bottom point + [9, 6], [9, 7], [9, 8], + [10, 7], +]); + +// All icons with labels +const ICONS = [ + { id: 'rocket-league', name: 'Gaming', dots: rocketLeagueCar, color: '#3B82F6' }, + { id: 'neural-network', name: 'AI/ML', dots: neuralNetwork, color: '#8B5CF6' }, + { id: 'microphone', name: 'Music', dots: microphone, color: '#EC4899' }, + { id: 'wanderlust', name: 'Travel', dots: wanderlust, color: '#10B981' }, + { id: 'lightbulb', name: 'Ideas', dots: lightbulb, color: '#F59E0B' }, + { id: 'rocket', name: 'Building', dots: rocket, color: '#EF4444' }, + { id: 'code', name: 'Code', dots: codeBrackets, color: '#06B6D4' }, + { id: 'heart', name: 'Passion', dots: heart, color: '#F43F5E' }, +]; + +// Duration each icon is shown (ms) +const ICON_DURATION = 3000; +// Transition duration for dots +const DOT_TRANSITION = 0.6; + +export default function DotMatrix({ className = '' }) { + const [currentIconIndex, setCurrentIconIndex] = useState(0); + const [isAnimating, setIsAnimating] = useState(false); + + // Cycle through icons + useEffect(() => { + const interval = setInterval(() => { + setIsAnimating(true); + setTimeout(() => { + setCurrentIconIndex((prev) => (prev + 1) % ICONS.length); + setIsAnimating(false); + }, 300); + }, ICON_DURATION); + + return () => clearInterval(interval); + }, []); + + const currentIcon = ICONS[currentIconIndex]; + + // Generate dots array + const dots = useMemo(() => { + return Array.from({ length: DOT_COUNT }, (_, i) => ({ + id: i, + row: Math.floor(i / GRID_SIZE), + col: i % GRID_SIZE, + })); + }, []); + + return ( +
+
+ + {dots.map((dot) => { + const isActive = currentIcon.dots.has(dot.id); + const delay = (dot.row + dot.col) * 0.02; + + return ( + + + + ); + })} + +
+ + {/* Icon label */} + + + + {currentIcon.name} + + + + + {/* Progress indicator */} +
+ {ICONS.map((icon, index) => ( +
+
+ ); +} diff --git a/components/redesign/DotMatrix.module.scss b/components/redesign/DotMatrix.module.scss new file mode 100644 index 000000000..53fa19c27 --- /dev/null +++ b/components/redesign/DotMatrix.module.scss @@ -0,0 +1,94 @@ +// Dot Matrix Animation Component Styles + +.container { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-6); +} + +.matrixWrapper { + position: relative; + width: 100%; + max-width: 280px; + aspect-ratio: 1; + + @media (max-width: 640px) { + max-width: 220px; + } +} + +.dotGrid { + display: grid; + gap: 4px; + width: 100%; + height: 100%; + padding: var(--space-2); + + @media (max-width: 640px) { + gap: 3px; + } +} + +.dotWrapper { + display: flex; + align-items: center; + justify-content: center; + aspect-ratio: 1; +} + +.dot { + width: 100%; + height: 100%; + border-radius: 50%; + background-color: var(--dot-inactive); + will-change: transform, opacity, background-color; +} + +.iconLabel { + height: 28px; + display: flex; + align-items: center; + justify-content: center; +} + +.labelText { + font-family: var(--font-mono); + font-size: var(--text-sm); + font-weight: 600; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.progressContainer { + display: flex; + gap: var(--space-2); + padding: var(--space-2); +} + +.progressDot { + width: 8px; + height: 8px; + border-radius: 50%; + border: none; + background-color: var(--border-color); + cursor: pointer; + transition: all var(--transition-base); + padding: 0; + + &:hover { + background-color: var(--text-muted); + transform: scale(1.2); + } + + &.active { + transform: scale(1.3); + } + + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +// Note: --dot-inactive variable is defined in variables-new.css diff --git a/components/redesign/Header.jsx b/components/redesign/Header.jsx new file mode 100644 index 000000000..045b0f3a7 --- /dev/null +++ b/components/redesign/Header.jsx @@ -0,0 +1,129 @@ +import { motion } from 'framer-motion'; +import { FaGithub, FaLinkedin, FaTwitter, FaEnvelope, FaArrowDown } from 'react-icons/fa'; +import DotMatrix from './DotMatrix'; +import styles from './Header.module.scss'; + +const socialLinks = [ + { + icon: FaGithub, + href: 'https://github.com/RiptideStar', + label: 'GitHub', + }, + { + icon: FaLinkedin, + href: 'https://www.linkedin.com/in/zhangkyle/', + label: 'LinkedIn', + }, + { + icon: FaTwitter, + href: 'https://twitter.com/', + label: 'Twitter', + }, +]; + +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.15, + delayChildren: 0.2, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.6, + ease: [0.25, 0.46, 0.45, 0.94], + }, + }, +}; + +export default function Header({ + name = "Kyle Zhang", + tagline = "Builder. Researcher. Creator.", + description = "M&T graduate from UPenn, passionate about building products at the intersection of gaming, AI, and human impact.", + email = "kyle@example.com" +}) { + const scrollToContent = () => { + const content = document.getElementById('about-section'); + if (content) { + content.scrollIntoView({ behavior: 'smooth' }); + } + }; + + return ( + +
+ {/* Dot Matrix Animation - Hero Element */} + + + + + {/* Name and Intro */} + +

{name}

+

{tagline}

+
+ + {/* Description */} + + {description} + + + {/* Social Links */} + + + + {email} + + +
+ {socialLinks.map((link) => ( + + + + ))} +
+
+ + {/* Scroll indicator */} + + + +
+
+ ); +} diff --git a/components/redesign/Header.module.scss b/components/redesign/Header.module.scss new file mode 100644 index 000000000..d1c8901d9 --- /dev/null +++ b/components/redesign/Header.module.scss @@ -0,0 +1,192 @@ +// Header Styles - Minimalist Hero with Dot Matrix + +.header { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: var(--space-8) var(--space-6); + position: relative; + + @media (max-width: 640px) { + min-height: calc(100vh - 60px); + padding: var(--space-6) var(--space-4); + } +} + +.heroContent { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + max-width: 600px; + gap: var(--space-8); + + @media (max-width: 640px) { + gap: var(--space-6); + } +} + +.dotMatrixWrapper { + margin-bottom: var(--space-4); +} + +.introSection { + display: flex; + flex-direction: column; + gap: var(--space-3); +} + +.name { + font-size: var(--text-5xl); + font-weight: 600; + letter-spacing: -0.03em; + color: var(--text-primary); + line-height: 1.1; + + @media (max-width: 640px) { + font-size: var(--text-4xl); + } +} + +.tagline { + font-family: var(--font-mono); + font-size: var(--text-lg); + color: var(--text-muted); + letter-spacing: 0.02em; + margin: 0; + + @media (max-width: 640px) { + font-size: var(--text-base); + } +} + +.description { + font-size: var(--text-lg); + color: var(--text-secondary); + line-height: 1.7; + max-width: 480px; + margin: 0; + + @media (max-width: 640px) { + font-size: var(--text-base); + } +} + +.socialRow { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-4); +} + +.emailLink { + display: inline-flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-3) var(--space-5); + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: var(--radius-full); + color: var(--text-secondary); + font-size: var(--text-sm); + transition: all var(--transition-base); + text-decoration: none; + + &:hover { + border-color: var(--border-hover); + color: var(--text-primary); + background: var(--bg-tertiary); + } + + svg { + width: 16px; + height: 16px; + color: var(--text-muted); + } +} + +.socialLinks { + display: flex; + gap: var(--space-3); +} + +.socialIcon { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + border-radius: 50%; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + color: var(--text-secondary); + transition: all var(--transition-base); + + &:hover { + border-color: var(--text-primary); + color: var(--text-primary); + background: var(--bg-tertiary); + } + + svg { + width: 20px; + height: 20px; + } +} + +.scrollIndicator { + position: absolute; + bottom: var(--space-8); + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + color: var(--text-muted); + cursor: pointer; + transition: all var(--transition-base); + animation: bounce 2s infinite; + + &:hover { + color: var(--text-primary); + } + + svg { + width: 16px; + height: 16px; + } + + @media (max-width: 640px) { + bottom: var(--space-6); + } +} + +@keyframes bounce { + 0%, 20%, 50%, 80%, 100% { + transform: translateX(-50%) translateY(0); + } + 40% { + transform: translateX(-50%) translateY(-8px); + } + 60% { + transform: translateX(-50%) translateY(-4px); + } +} + +// Dark mode adjustments +:global([data-theme='dark']) { + .emailLink, + .socialIcon { + background: var(--bg-tertiary); + border-color: var(--border-color); + + &:hover { + background: var(--bg-card); + border-color: var(--border-hover); + } + } +} diff --git a/components/redesign/Layout.jsx b/components/redesign/Layout.jsx new file mode 100644 index 000000000..d2d1e2923 --- /dev/null +++ b/components/redesign/Layout.jsx @@ -0,0 +1,29 @@ +import { motion, AnimatePresence } from 'framer-motion'; +import ThemeToggle from './ThemeToggle'; +import styles from './Layout.module.scss'; + +export default function Layout({ children }) { + return ( +
+ + + + {children} + + +
+
+

Designed & built by Kyle Zhang

+

© {new Date().getFullYear()}

+
+
+
+ ); +} diff --git a/components/redesign/Layout.module.scss b/components/redesign/Layout.module.scss new file mode 100644 index 000000000..1598ea4c3 --- /dev/null +++ b/components/redesign/Layout.module.scss @@ -0,0 +1,47 @@ +// Layout Styles - Minimalist Portfolio + +.layout { + min-height: 100vh; + display: flex; + flex-direction: column; + background: var(--bg-primary); +} + +.main { + flex: 1; +} + +.container { + width: 100%; + max-width: var(--max-width); + margin: 0 auto; + padding: 0 var(--space-6); + + @media (max-width: 640px) { + padding: 0 var(--space-4); + } +} + +.footer { + padding: var(--space-16) var(--space-6); + border-top: 1px solid var(--border-color); + + .container { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-2); + } + + p { + color: var(--text-muted); + font-size: var(--text-sm); + text-align: center; + margin: 0; + } + + .copyright { + font-size: var(--text-xs); + color: var(--text-faint); + } +} diff --git a/components/redesign/ThemeToggle.jsx b/components/redesign/ThemeToggle.jsx new file mode 100644 index 000000000..3ded77639 --- /dev/null +++ b/components/redesign/ThemeToggle.jsx @@ -0,0 +1,64 @@ +import { useState, useEffect } from 'react'; +import { motion } from 'framer-motion'; +import { FaSun, FaMoon } from 'react-icons/fa'; +import styles from './ThemeToggle.module.scss'; + +export default function ThemeToggle() { + const [theme, setTheme] = useState('light'); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + // Check localStorage or system preference + const savedTheme = localStorage.getItem('theme'); + const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + + if (savedTheme) { + setTheme(savedTheme); + document.documentElement.setAttribute('data-theme', savedTheme); + } else if (systemPrefersDark) { + setTheme('dark'); + document.documentElement.setAttribute('data-theme', 'dark'); + } + }, []); + + const toggleTheme = () => { + const newTheme = theme === 'light' ? 'dark' : 'light'; + setTheme(newTheme); + document.documentElement.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); + }; + + // Prevent hydration mismatch + if (!mounted) { + return ( +
+ +
+ ); + } + + return ( +
+ + + {theme === 'light' ? : } + + +
+ ); +} diff --git a/components/redesign/ThemeToggle.module.scss b/components/redesign/ThemeToggle.module.scss new file mode 100644 index 000000000..8b796786c --- /dev/null +++ b/components/redesign/ThemeToggle.module.scss @@ -0,0 +1,58 @@ +// Theme Toggle - Minimalist style + +.toggleWrapper { + position: fixed; + top: var(--space-6); + right: var(--space-6); + z-index: 1000; + + @media (max-width: 640px) { + top: var(--space-4); + right: var(--space-4); + } +} + +.toggle { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + border: 1px solid var(--border-color); + border-radius: 50%; + background: var(--bg-secondary); + color: var(--text-muted); + cursor: pointer; + transition: all var(--transition-base); + backdrop-filter: blur(8px); + + &:hover { + border-color: var(--border-hover); + color: var(--text-primary); + background: var(--bg-tertiary); + } + + span { + display: flex; + align-items: center; + justify-content: center; + } + + svg { + width: 18px; + height: 18px; + } +} + +// Dark mode adjustments +:global([data-theme='dark']) { + .toggle { + background: var(--bg-tertiary); + border-color: var(--border-color); + + &:hover { + background: var(--bg-card); + border-color: var(--border-hover); + } + } +} diff --git a/components/redesign/sections/About.jsx b/components/redesign/sections/About.jsx new file mode 100644 index 000000000..832c1f50f --- /dev/null +++ b/components/redesign/sections/About.jsx @@ -0,0 +1,73 @@ +import { motion } from 'framer-motion'; +import { useInView } from 'react-intersection-observer'; +import styles from './Sections.module.scss'; + +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.5, ease: 'easeOut' }, + }, +}; + +export default function About({ data }) { + const [ref, inView] = useInView({ + triggerOnce: true, + threshold: 0.1, + }); + + const { intro, highlights = [] } = data || {}; + + return ( + +
+ + 01 +

About

+
+ + +

{intro}

+ + {highlights.length > 0 && ( + + {highlights.map((highlight, index) => ( + + {highlight.link ? ( + <> + {highlight.text} + + {highlight.link.text} + + {highlight.suffix} + + ) : ( + highlight.text + )} + + ))} + + )} +
+
+
+ ); +} diff --git a/components/redesign/sections/Contact.jsx b/components/redesign/sections/Contact.jsx new file mode 100644 index 000000000..601fb826f --- /dev/null +++ b/components/redesign/sections/Contact.jsx @@ -0,0 +1,103 @@ +import { motion } from 'framer-motion'; +import { useInView } from 'react-intersection-observer'; +import { FaEnvelope, FaLinkedin, FaGithub, FaTwitter } from 'react-icons/fa'; +import styles from './Sections.module.scss'; + +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.5, ease: 'easeOut' }, + }, +}; + +const socialLinks = [ + { + icon: FaEnvelope, + href: 'mailto:kyle@example.com', + label: 'Email', + text: 'kyle@example.com', + }, + { + icon: FaLinkedin, + href: 'https://www.linkedin.com/in/zhangkyle/', + label: 'LinkedIn', + text: 'linkedin.com/in/zhangkyle', + }, + { + icon: FaGithub, + href: 'https://github.com/RiptideStar', + label: 'GitHub', + text: 'github.com/RiptideStar', + }, + { + icon: FaTwitter, + href: 'https://twitter.com/', + label: 'Twitter', + text: '@kylezhang', + }, +]; + +export default function Contact({ data }) { + const [ref, inView] = useInView({ + triggerOnce: true, + threshold: 0.1, + }); + + const { message } = data || {}; + + return ( + +
+ + 03 +

Get in Touch

+
+ + +

+ {message || "I'm always excited to connect with fellow builders, researchers, and anyone passionate about technology. Whether you have a project idea, want to collaborate, or just want to chat—reach out!"} +

+
+ + + {socialLinks.map((link) => ( + + +
+ {link.label} + {link.text} +
+
+ ))} +
+
+
+ ); +} diff --git a/components/redesign/sections/Experience.jsx b/components/redesign/sections/Experience.jsx new file mode 100644 index 000000000..1d047fb0c --- /dev/null +++ b/components/redesign/sections/Experience.jsx @@ -0,0 +1,88 @@ +import { motion } from 'framer-motion'; +import Card from '../Card'; +import styles from './Sections.module.scss'; + +const listVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, x: -10 }, + visible: { + opacity: 1, + x: 0, + transition: { duration: 0.4 }, + }, +}; + +export default function Experience({ data }) { + const { intro, education = [], internships = [] } = data; + + return ( + +

{intro}

+ + {education.length > 0 && ( + <> +

Education

+ + {education.map((item, index) => ( + + {item.link ? ( + + {item.name} + + ) : ( + {item.name} + )} + {item.description && ( + — {item.description} + )} + + ))} + + + )} + + {internships.length > 0 && ( + <> +

Internships

+ + {internships.map((item, index) => ( + + {item.link ? ( + + {item.name} + + ) : ( + {item.name} + )} + {item.description && ( + , {item.description} + )} + + ))} + + + )} +
+ ); +} diff --git a/components/redesign/sections/Present.jsx b/components/redesign/sections/Present.jsx new file mode 100644 index 000000000..132917a43 --- /dev/null +++ b/components/redesign/sections/Present.jsx @@ -0,0 +1,62 @@ +import { motion } from 'framer-motion'; +import Card from '../Card'; +import styles from './Sections.module.scss'; + +const listVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, x: -10 }, + visible: { + opacity: 1, + x: 0, + transition: { duration: 0.4 }, + }, +}; + +export default function Present({ data }) { + const { intro, updates = [] } = data; + + return ( + +

+ {intro} +

+ + {updates.length > 0 && ( + <> +

Updates

+ + {updates.map((update, index) => ( + + {update.date} + + {update.text} + {update.link && ( + + {update.link.text} + + )} + {update.suffix} + + + ))} + + + )} +
+ ); +} diff --git a/components/redesign/sections/Projects.jsx b/components/redesign/sections/Projects.jsx new file mode 100644 index 000000000..2fe194c75 --- /dev/null +++ b/components/redesign/sections/Projects.jsx @@ -0,0 +1,101 @@ +import { motion } from 'framer-motion'; +import { FaExternalLinkAlt, FaGithub } from 'react-icons/fa'; +import Card from '../Card'; +import styles from './Sections.module.scss'; + +const projectVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.15, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.5 }, + }, +}; + +export default function Projects({ data }) { + const projects = data || []; + + return ( + + + {projects.map((project, index) => ( + +
+

+ {project.url ? ( + + {project.project} + + ) : ( + project.project + )} +

+
+ {project.url && ( + + + + )} + {project.repo && project.repo !== 'Private' && ( + + + + )} +
+
+ +

+ {project.descriptionTitle} {project.description} +

+ + {project.stack && project.stack.length > 0 && ( +
+ {project.stack.slice(0, 6).map((tech, i) => ( + + {tech.name} + + ))} + {project.stack.length > 6 && ( + +{project.stack.length - 6} + )} +
+ )} +
+ ))} +
+
+ ); +} diff --git a/components/redesign/sections/Sections.module.scss b/components/redesign/sections/Sections.module.scss new file mode 100644 index 000000000..cb444488a --- /dev/null +++ b/components/redesign/sections/Sections.module.scss @@ -0,0 +1,481 @@ +// Section Styles - Minimalist Portfolio + +// Base section styles +.section { + padding: var(--space-24) var(--space-6); + border-top: 1px solid var(--border-color); + + @media (max-width: 640px) { + padding: var(--space-16) var(--space-4); + } +} + +.container { + max-width: var(--max-width); + margin: 0 auto; +} + +// Section header with number +.sectionHeader { + display: flex; + align-items: center; + gap: var(--space-4); + margin-bottom: var(--space-10); + + @media (max-width: 640px) { + margin-bottom: var(--space-8); + } +} + +.sectionNumber { + font-family: var(--font-mono); + font-size: var(--text-sm); + color: var(--text-muted); + font-weight: 500; +} + +.sectionTitle { + font-size: var(--text-2xl); + font-weight: 600; + color: var(--text-primary); + margin: 0; +} + +// Content area +.content { + max-width: var(--max-width-narrow); +} + +// Introduction text +.intro { + font-size: var(--text-lg); + color: var(--text-secondary); + line-height: 1.8; + margin-bottom: var(--space-6); + + @media (max-width: 640px) { + font-size: var(--text-base); + } +} + +// Highlight list +.highlightList { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: var(--space-4); + + li { + display: flex; + align-items: flex-start; + gap: var(--space-3); + color: var(--text-secondary); + line-height: 1.6; + + &::before { + content: "→"; + color: var(--text-muted); + flex-shrink: 0; + margin-top: 2px; + } + + a { + color: var(--link-color); + font-weight: 500; + + &:hover { + text-decoration: underline; + } + } + } +} + +// Project list +.projectList { + display: flex; + flex-direction: column; + gap: var(--space-10); +} + +.projectItem { + padding-bottom: var(--space-10); + border-bottom: 1px solid var(--border-color); + + &:last-child { + border-bottom: none; + padding-bottom: 0; + } +} + +.projectMeta { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--space-3); +} + +.projectType { + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); +} + +.projectLinks { + display: flex; + gap: var(--space-3); + + a { + color: var(--text-muted); + transition: color var(--transition-fast); + + &:hover { + color: var(--text-primary); + } + + svg { + width: 16px; + height: 16px; + } + } +} + +.projectName { + font-size: var(--text-xl); + font-weight: 600; + margin-bottom: var(--space-3); + + a { + color: var(--text-primary); + text-decoration: none; + transition: color var(--transition-fast); + + &:hover { + color: var(--link-color); + } + } +} + +.projectDescription { + color: var(--text-secondary); + line-height: 1.7; + margin-bottom: var(--space-4); + + strong { + color: var(--text-primary); + font-weight: 500; + } +} + +.techStack { + display: flex; + flex-wrap: wrap; + gap: var(--space-2); +} + +.techTag { + font-family: var(--font-mono); + font-size: var(--text-xs); + padding: var(--space-1) var(--space-3); + background: var(--bg-tertiary); + border-radius: var(--radius-sm); + color: var(--text-muted); +} + +.techMore { + font-family: var(--font-mono); + font-size: var(--text-xs); + color: var(--text-faint); +} + +// Contact section +.contactIntro { + font-size: var(--text-lg); + color: var(--text-secondary); + line-height: 1.8; + margin-bottom: var(--space-8); + max-width: var(--max-width-narrow); + + @media (max-width: 640px) { + font-size: var(--text-base); + } +} + +.contactGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--space-4); + + @media (max-width: 640px) { + grid-template-columns: 1fr; + } +} + +.contactCard { + display: flex; + align-items: center; + gap: var(--space-4); + padding: var(--space-5); + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + text-decoration: none; + transition: all var(--transition-base); + + &:hover { + border-color: var(--border-hover); + background: var(--bg-tertiary); + } +} + +.contactIcon { + width: 24px; + height: 24px; + color: var(--text-muted); + flex-shrink: 0; +} + +.contactInfo { + display: flex; + flex-direction: column; + gap: var(--space-1); + min-width: 0; +} + +.contactLabel { + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); +} + +.contactText { + font-size: var(--text-sm); + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +// Existing legacy styles (keeping for backwards compatibility) +.mainText { + color: var(--text-secondary); + line-height: 1.7; + margin-bottom: var(--space-4); + + a { + color: var(--link-color); + + &:hover { + text-decoration: underline; + } + } + + &:last-child { + margin-bottom: 0; + } +} + +.subheading { + font-size: var(--text-lg); + font-weight: 600; + color: var(--text-primary); + margin-top: var(--space-6); + margin-bottom: var(--space-3); +} + +.updateList { + list-style: disc; + padding-left: var(--space-6); + + li { + margin-bottom: var(--space-2); + color: var(--text-secondary); + line-height: 1.6; + + &::marker { + color: var(--text-muted); + } + } +} + +.updateDate { + color: var(--text-muted); + font-weight: 500; + + &::after { + content: ": "; + } +} + +.updateText { + a { + color: var(--link-color); + + &:hover { + text-decoration: underline; + } + } +} + +.itemList { + list-style: disc; + padding-left: var(--space-6); + + li { + margin-bottom: var(--space-3); + color: var(--text-secondary); + line-height: 1.6; + + &::marker { + color: var(--text-muted); + } + + a { + color: var(--link-color); + font-weight: 600; + + &:hover { + text-decoration: underline; + } + } + + strong { + color: var(--text-primary); + } + } +} + +.itemDescription { + color: var(--text-secondary); +} + +.projectGrid { + display: flex; + flex-direction: column; + gap: var(--space-6); +} + +.projectCard { + padding: var(--space-5); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + background: var(--bg-secondary); + transition: all var(--transition-base); + + &:hover { + border-color: var(--border-hover); + box-shadow: var(--shadow-sm); + } +} + +.projectHeader { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: var(--space-3); +} + +.projectTitle { + font-size: var(--text-lg); + font-weight: 600; + margin: 0; + + a { + color: var(--text-primary); + text-decoration: none; + + &:hover { + color: var(--link-color); + } + } +} + +.projectLink { + color: var(--text-muted); + transition: color var(--transition-fast); + + &:hover { + color: var(--link-color); + } + + svg { + width: 16px; + height: 16px; + } +} + +.skillsContainer { + display: flex; + flex-direction: column; + gap: var(--space-6); +} + +.skillCategory { + &:not(:last-child) { + padding-bottom: var(--space-6); + border-bottom: 1px solid var(--border-color); + } +} + +.skillCategoryTitle { + font-size: var(--text-base); + font-weight: 600; + color: var(--text-primary); + margin-bottom: var(--space-3); +} + +.skillTags { + display: flex; + flex-wrap: wrap; + gap: var(--space-2); +} + +.skillTag { + font-size: var(--text-sm); + padding: var(--space-2) var(--space-4); + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + color: var(--text-secondary); + transition: all var(--transition-fast); + + &:hover { + border-color: var(--border-hover); + color: var(--text-primary); + } +} + +.contactLinks { + display: flex; + gap: var(--space-4); + margin-top: var(--space-4); + flex-wrap: wrap; +} + +.contactLink { + color: var(--link-color); + font-weight: 500; + + &:hover { + text-decoration: underline; + } +} + +// Dark mode +:global([data-theme='dark']) { + .contactCard { + background: var(--bg-tertiary); + + &:hover { + background: var(--bg-card); + } + } + + .techTag { + background: var(--bg-card); + } +} diff --git a/components/redesign/sections/Skills.jsx b/components/redesign/sections/Skills.jsx new file mode 100644 index 000000000..46a1e52db --- /dev/null +++ b/components/redesign/sections/Skills.jsx @@ -0,0 +1,55 @@ +import { motion } from 'framer-motion'; +import Card from '../Card'; +import styles from './Sections.module.scss'; + +const skillVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.05, + }, + }, +}; + +const tagVariants = { + hidden: { opacity: 0, scale: 0.8 }, + visible: { + opacity: 1, + scale: 1, + transition: { duration: 0.3 }, + }, +}; + +export default function Skills({ data }) { + const categories = data || []; + + return ( + +
+ {categories.map((category, index) => ( +
+

{category.title}

+ + {category.skills.map((skill, i) => ( + + {skill} + + ))} + +
+ ))} +
+
+ ); +} diff --git a/components/redesign/sections/Work.jsx b/components/redesign/sections/Work.jsx new file mode 100644 index 000000000..ea1615fa7 --- /dev/null +++ b/components/redesign/sections/Work.jsx @@ -0,0 +1,117 @@ +import { motion } from 'framer-motion'; +import { useInView } from 'react-intersection-observer'; +import { FaExternalLinkAlt, FaGithub } from 'react-icons/fa'; +import styles from './Sections.module.scss'; + +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.15, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.5, ease: 'easeOut' }, + }, +}; + +export default function Work({ data }) { + const [ref, inView] = useInView({ + triggerOnce: true, + threshold: 0.1, + }); + + const projects = data || []; + + return ( + +
+ + 02 +

Work

+
+ + + {projects.map((project, index) => ( + +
+ + {project.type || 'Project'} + +
+ {project.url && ( + + + + )} + {project.repo && project.repo !== 'Private' && ( + + + + )} +
+
+ +

+ {project.url ? ( + + {project.project} + + ) : ( + project.project + )} +

+ +

+ {project.descriptionTitle} {project.description} +

+ + {project.stack && project.stack.length > 0 && ( +
+ {project.stack.slice(0, 5).map((tech, i) => ( + + {tech.name} + + ))} + {project.stack.length > 5 && ( + + +{project.stack.length - 5} + + )} +
+ )} +
+ ))} +
+
+
+ ); +} diff --git a/components/sections/index/about.jsx b/components/sections/index/about.jsx deleted file mode 100644 index aa507db8e..000000000 --- a/components/sections/index/about.jsx +++ /dev/null @@ -1,73 +0,0 @@ -// Core packages -import Image from 'next/image' - -// Section structure -import Section from '../../structure/section'; -import Container from '../../structure/container'; - -// Section general blocks -import SectionTitle from '../../blocks/section.title.block' -import SectionGridBg from '../../blocks/section.grid.block' - -// Section specific blocks -import BadgesBlock from '../../blocks/about.badges.block' -import CopyBlock from '../../blocks/about.copy.block' - -// Section scss -import about from '../../../styles/sections/index/about.module.scss'; - -/** - * Section: About - * An overview of yourself. - * Highlight your top level attributes and disciplines. - * - * @returns {jsx} - */ -export default function About() { - return ( -
- - -
-
- Nelson family photo - {/* */} -
-
- - -
-
-
-
- ) -} - -const methods = [ - { key: 'planet-moon', name: 'User Research', type: 'fad' }, - { key: 'qrcode', name: 'Digital Strategy', type: 'fad' }, - { key: 'window', name: 'Design Systems', type: 'fad' }, - { key: 'cubes', name: 'Product Strategy', type: 'far' }, - { key: 'layer-plus', name: 'Brand Strategy', type: 'fad' }, - { key: 'solar-system', name: 'Operations', type: 'fad' }, -] \ No newline at end of file diff --git a/components/sections/index/career.jsx b/components/sections/index/career.jsx deleted file mode 100644 index f9d8fe06e..000000000 --- a/components/sections/index/career.jsx +++ /dev/null @@ -1,191 +0,0 @@ -// Core packages -import Image from 'next/image' - -import Badges from '../../utils/badge.list.util' - -// Section structure -import Section from '../../structure/section'; -import Container from '../../structure/container'; - -// Section general blocks -import SectionTitle from '../../blocks/section.title.block' -import SectionGridBg from '../../blocks/section.grid.block' - -// Career scss -import career from '../../../styles/sections/index/career.module.scss' - -/** - * Section: Career - * - * @returns {jsx} - */ -export default function Career() { - return ( -
- - -
-
-
- -

My Supply Co.

-

Permanent Full-time

-

Apr 2019 - Present · 3 yrs 10 mos

-
Vancouver, British Columbia, Canada
-
-

- My Supply Co. helps Canadians manage mental and physical health with naturally occurring nootropic and adaptogenic products. They carry products with complex attributes, aiming to solve a large variety of personal care needs — this requires an extremely customized and evolving approach to how the store is built and functions. -

-
-
-
- -
-
-
- -

Director of Product Design and Development

-

Nov 2021 - Present · 1 yrs 3 mos

-
-

- I am responsible for the ideation, planning, and development of new consumer goods—and customer and employee facing microservice software. During these projects I work with key stakeholders within our company and supplychain to ensure and meet quality goals across multiple domains. -

-
-
-
- -
-
- -

Full Stack Developer & User Experience Designer

-

Feb 2020 - Nov 2021 · 1 yrs 10 mos

-
-

- As the lead full stack developer I am responsible for all software development, CI/CD, and QA. This is for the front end, APIs, and the back end. Additionally I was tasked with identifying and analyzing weak points in the customer journey and employee workflows. Each project had to be estimated and prioritized based on its workload and immediate impact to efficiency or revenue. Some of these projects have been so successful internally that we have planned refactoring for commercialization. -

-

- Some key projects complete during this time 👇 -

-
    -
  • - Product attribute and settings automated testing - Eradicated critical data input errors -
  • -
  • - Inventory management reporting and automation - Decreased purchasing labour by ~80% -
  • -
  • - Sales management plugin with AJAX shopping cart integration - Increased AOV by 8.3% -
  • -
  • - Bespoke ID verification software and WooCommerce integration - Decreased Credit Card fraud by 98% -
  • -
- -
-
-
- -
-
- -

Front End Developer & User Interface Designer

-

Apr 2019 - Feb 2020 · 11 mos

-
-

- I was brought on to help fill multiple creative rolls in a small start-up environment. Working with the marketing team to create the brand and logos — designing and developing a new front end for the website — and improving the users experience and store KPIs through design and merchandising optimizations. -

-

- Some key projects completed during this time 👇 -

-
    -
  • - Full functionality interactive shopping cart to replace cart page - Increased conversions by 0.7% -
  • -
  • Complex multi-state animated menus represented in an elegant UI - Strong brand confidence booster with state of the art menu -
  • -
  • - Design and development of the site and merchandising strategy optimized for market - 7.1% overall conversion rate -
  • -
- -
-
-
-
- -
-
- -

Another Creative Ltd.

-

Contract Part-time

-

Jun 2016 - Present · 6 yrs 8 mos

-
Vancouver, British Columbia, Canada
-
-

- Another Creative is a full stack agency that helps deliver exceptional digital experiences to small and medium businesses. Branding, Marketing, and Web/Software Development. -

-
-
-
- -
-
- -

West Coast Electronics

-

Permanent Full-time

-

Jan 2006 - Nov 2011 · 5 yrs 11 mos

-
Vancouver, British Columbia, Canada
-
-

I was the Electronics Wizard 🧙‍♂️

-

West Coast Electronics was a repair shop that fixed computers, consoles, and cell phones.

-
-
-
-
-
-
- ) -} - -const fullStack = [ - { key: 'javascript', name: 'JavaScript', type: 'devicon' }, - { key: 'nodejs', name: 'NodeJS', type: 'devicon' }, - { key: 'react', name: 'React', type: 'devicon' }, - { key: 'nextjs', name: 'NextJS', type: 'devicon' }, - { key: 'php', name: 'PHP', type: 'devicon' }, - { key: 'wordpress', name: 'WordPress', type: 'devicon' }, - { key: 'woocommerce', name: 'WooCommerce', type: 'devicon' }, - { key: 'html5', name: 'HTML5', type: 'devicon' }, - { key: 'css3', name: 'CSS3', type: 'devicon' }, - { key: 'sass', name: 'SASS', type: 'devicon' }, - { key: 'git', name: 'Git', type: 'devicon' }, - { key: 'mysql', name: 'MySQL', type: 'devicon' }, - { key: 'mongodb', name: 'MongoDB', type: 'devicon' }, -] - -const stack = [ - { key: 'javascript', name: 'JavaScript', type: 'devicon' }, - { key: 'nodejs', name: 'NodeJS', type: 'devicon' }, - { key: 'react', name: 'React', type: 'devicon' }, - { key: 'nextjs', name: 'NextJS', type: 'devicon' }, - { key: 'php', name: 'PHP', type: 'devicon' }, - { key: 'wordpress', name: 'WordPress', type: 'devicon' }, - { key: 'woocommerce', name: 'WooCommerce', type: 'devicon' }, - { key: 'html5', name: 'HTML5', type: 'devicon' }, - { key: 'css3', name: 'CSS3', type: 'devicon' }, - { key: 'sass', name: 'SASS', type: 'devicon' }, - { key: 'git', name: 'Git', type: 'devicon' }, - { key: 'mysql', name: 'MySQL', type: 'devicon' }, - { key: 'mongodb', name: 'MongoDB', type: 'devicon' }, -] \ No newline at end of file diff --git a/components/sections/index/hero.jsx b/components/sections/index/hero.jsx deleted file mode 100644 index ff05c3440..000000000 --- a/components/sections/index/hero.jsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useState } from 'react'; -import { TypeAnimation } from 'react-type-animation'; - -import Section from '../../structure/section'; -import Container from '../../structure/container'; - -import space from '../../utils/spacing.util'; - -import Icon from '../../utils/icon.util' - -import HeroBg from '../../blocks/hero.bg/bg-color-1'; - -import hero from '../../../styles/sections/index/hero.module.scss'; -import button from '../../../styles/blocks/button.module.scss'; - -import content from '../../../content/index/hero.json' - - -/** - * TO DO LIST - * - * - Create a typog.modules.scss - * Load this module onto every component, and use predefined typography classes to keep typography consistent - * - * - space.modules.scss - * Load this module onto every component, and use predefined spacial classes to keep geometry consistent - */ - -export default function Hero() { - - const [typingStatus, setTypingStatus] = useState('Initializing'); - - return ( -
- - { setTypingStatus('typing') }, - content.intro.start, - () => { setTypingStatus('typed') }, - content.intro.deleteDelay, - () => { setTypingStatus('deleting') }, - content.intro.end, - () => { setTypingStatus('deleted') }, - content.intro.restartDelay, - ]} - speed={content.intro.speed} - deletionSpeed={content.intro.deletionSpeed} - wrapper={content.intro.wrapper} - repeat={Infinity} - /> -
-

- {content.header.name} -

-

- {content.header.usp} -

-
-
-

- { content.paragraph } -

-
-
- - -
-
- -
- ) -} \ No newline at end of file diff --git a/components/sections/index/looking.jsx b/components/sections/index/looking.jsx deleted file mode 100644 index 443881aa4..000000000 --- a/components/sections/index/looking.jsx +++ /dev/null @@ -1,37 +0,0 @@ -// Section structure -import Section from '../../structure/section'; -import Container from '../../structure/container'; - -// Specing util -import Spacing from '../../utils/spacing.util'; - -// Section general blocks -import SectionGridBg from '../../blocks/section.grid.block' -import SectionTitle from '../../blocks/section.title.block' - -// Section scss -import looking from '../../../styles/sections/index/looking.module.scss'; -import section from '../../../styles/blocks/section.title.module.scss' - -/** - * Section: Looking - * Declare your employment intentions 🚀 - * - * @returns {jsx} - */ -export default function Looking() { - return ( -
- -

I'm currently looking for employment.

-

Senior : {

-

Front End Engineer,

-

User Experience Designer

-

}

-

I am particularily interested in product facing postions where I can help make an organization wide impact.

- {/*

Senior User Experience Designer

*/} - {/*

with a focus on Product Design.

*/} -
-
- ) -} \ No newline at end of file diff --git a/components/sections/index/technical.jsx b/components/sections/index/technical.jsx deleted file mode 100644 index 5b3fc85b0..000000000 --- a/components/sections/index/technical.jsx +++ /dev/null @@ -1,103 +0,0 @@ -// Core packages -import Image from 'next/image' - -// Section structure -import Section from '../../structure/section'; -import Container from '../../structure/container'; - -// Section general blocks -import SectionTitle from '../../blocks/section.title.block' -import SectionGridBg from '../../blocks/section.grid.block' - -// Section specific blocks -import BadgesBlock from '../../blocks/about.badges.block' -import CopyBlock from '../../blocks/about.copy.block' - -// Section scss -import about from '../../../styles/sections/index/about.module.scss' - -/** - * Section: Technical - * Highlight your technical skills with a short blurb about you, - * Then display the programs you are proficient with and the technologies you use if applicable. - * - * @returns {jsx} - */ -export default function Technical() { - return ( -
- - -
-
- - - -
-
- Data Strings 01 by Colorpong: https://ywft.us/2177b695b -
-
-
- {/* */} -
- ) -} - -const software = [ - { key: 'photoshop', name: 'Photoshop', type: 'devicon' }, - { key: 'illustrator', name: 'Illustrator', type: 'devicon' }, - { key: 'figma', name: 'Figma', type: 'devicon' }, - { key: 'vscode', name: 'VSCode', type: 'devicon' }, - { key: 'mailbox', name: 'Postman', type: 'fas' }, - { key: 'computer-mouse',name: 'Click Up', type: 'fas' }, - { key: 'list-music', name: 'Ableton', type: 'fas' }, - { key: 'aftereffects', name: 'After Effects', type: 'devicon' }, - { key: 'premierepro', name: 'Premiere Pro', type: 'devicon' }, -] - -const tech = [ - { key: 'javascript', name: 'JavaScript', type: 'devicon' }, - { key: 'nodejs', name: 'NodeJS', type: 'devicon' }, - { key: 'react', name: 'React', type: 'devicon' }, - { key: 'nextjs', name: 'NextJS', type: 'devicon' }, - { key: 'jquery', name: 'jQuery', type: 'devicon' }, - { key: 'php', name: 'PHP', type: 'devicon' }, - { key: 'wordpress', name: 'WordPress', type: 'devicon' }, - { key: 'woocommerce', name: 'WooCommerce', type: 'devicon' }, - { key: "google", name: "GA4/GTM", type: "devicon" }, - { key: 'html5', name: 'HTML5', type: 'devicon' }, - { key: 'css3', name: 'CSS3', type: 'devicon' }, - { key: 'sass', name: 'SASS', type: 'devicon' }, - { key: 'git', name: 'Git', type: 'devicon' }, - { key: 'mysql', name: 'MySQL', type: 'devicon' }, - { key: 'mongodb', name: 'MongoDB', type: 'devicon' }, -] \ No newline at end of file diff --git a/components/sections/projects/featured.jsx b/components/sections/projects/featured.jsx index 10f0c018b..d159153d8 100644 --- a/components/sections/projects/featured.jsx +++ b/components/sections/projects/featured.jsx @@ -18,8 +18,8 @@ export default function FeaturedProjects() { { content.map( (data, index) => { return ( diff --git a/content/_settings.json b/content/_settings.json index 7567f7853..7f8ee89b0 100644 --- a/content/_settings.json +++ b/content/_settings.json @@ -1,9 +1,9 @@ { - "name": "Andrew Nelson", + "name": "Kyle Zhang", "logo": "", "username": { - "github": "atlamors", + "github": "RiptideStar", "medium": "@--andrewnelson" }, diff --git a/content/footer.json b/content/footer.json index f72f02857..4ae90eace 100644 --- a/content/footer.json +++ b/content/footer.json @@ -1,50 +1,27 @@ { "acknowledgments": [ { - "person": "Jihad Hassan - Marketing Director", - "link": "", + "person": "Gad Allon - Professor, Director of M&T", + "link": "https://oid.wharton.upenn.edu/profile/gadallon/", "note": "Thank you for all the advice and feedback." - }, - { - "person": "Colorpong - Artist", - "link": "ttps://www.youworkforthem.com/designer/536/colorpong?aff=1115", - "note": "Checkout their amazing vector illustrations." - }, - { - "person": "Brittany Chiang - Software Engineer", - "link": "https://brittanychiang.com/", - "note": "A major inspiration to create an open source theme." - }, - { - "person": "Vercel - Platform", - "link": "https://vercel.com/docs", - "note": "Host your own Next.js project for free!" } ], "links": [ { - "person": "YWFT Creative Marketplace", - "link": "https://www.youworkforthem.com/?aff=1115", - "note": "Best in class creative assets." + "person": "Jerome Fisher Program in Management & Technology", + "link": "https://fisher.wharton.upenn.edu/", + "note": "Dual degree program offering BSE/BAS from Penn Engineering and BS in Economics from Wharton." } ], "social": [ { - "url": "https://medium.com/@--andrewnelson", - "icon": "medium" - }, - { - "url": "https://dev.to/andrewnelson", - "icon": "dev" - }, - { - "url": "https://www.linkedin.com/in/--andrewnelson/", + "url": "https://www.linkedin.com/in/zhangkyle/", "icon": "linkedin" }, { - "url": "https://github.com/atlamors", + "url": "https://github.com/RiptideStar", "icon": "github" } ] diff --git a/content/index/hero.json b/content/index/hero.json index d11d50feb..39a8222b2 100644 --- a/content/index/hero.json +++ b/content/index/hero.json @@ -1,6 +1,6 @@ { "intro": { - "start": "const Andrew = ( name, passion ) =>", + "start": "founder, designer, engineer", "end": "Hello, my name is", "speed": 60, "deletionSpeed": 80, @@ -11,11 +11,11 @@ }, "header": { - "name": "Andrew Nelson.", + "name": "Kyle Zhang.", "usp": "I design and build meaningful experiences." }, - "paragraph": "I am a digital polymath — a constantly evolving digital creator driven by a passion for lifelong learning and the desire to leave a lasting impact.", + "paragraph": "Entrepreneurial UPenn M&T graduate, dabbling into everything from full stack to AI, for the gaming and healthcare industries.", "buttons": { "primary": { @@ -25,7 +25,7 @@ }, "secondary": { "title": "LinkedIn", - "url": "", + "url": "https://www.linkedin.com/in/zhangkyle/", "leaveSite": "true" } } diff --git a/content/navbar.json b/content/navbar.json index d1e27782d..726142c91 100644 --- a/content/navbar.json +++ b/content/navbar.json @@ -1,18 +1,7 @@ [ { - "url": "/", - "title": "About Me" - }, - { - "url": "/case-studies", - "title": "Case Studies" - }, - { - "url": "/articles", - "title": "Articles" - }, - { - "url": "/projects", - "title": "Projects" + "url": "/links", + "title": "Links", + "id": "links" } ] \ No newline at end of file diff --git a/content/projects/featured.json b/content/projects/featured.json index 34e0a010e..357d29c86 100644 --- a/content/projects/featured.json +++ b/content/projects/featured.json @@ -1,53 +1,60 @@ [ - { - "project": "My Supply Co.", - "url": "https://mysupplyco.com", - "repo": "Private", - "descriptionTitle": "D2C & B2B ecommerce site and blog", - "description": "with elegant solutions for a complex codebase and customer journey.", - "imageOptions": [ - ], - "images": [ - { "key": "mock-stack", "hover": "right", "h": "1200", "w": "556", "url": "/img/msc-mock_stack/03.png" }, - { "key": "mock-stack", "hover": "right", "h": "1200", "w": "556", "url": "/img/msc-mock_stack/02.png" }, - { "key": "mock-stack", "hover": "left", "h": "1200", "w": "556", "url": "/img/msc-mock_stack/01.png" } - ], - "stack": [ - { "key": "php", "name": "PHP", "type": "devicon" }, - { "key": "mysql", "name": "mySQL", "type": "devicon" }, - { "key": "javascript", "name": "JavaScript", "type": "devicon" }, - { "key": "jquery", "name": "jQuery", "type": "devicon" }, - { "key": "woocommerce", "name": "WooCommerce", "type": "devicon" }, - { "key": "wordpress", "name": "Wordpress", "type": "devicon" }, - { "key": "html5", "name": "HTML5", "type": "devicon" }, - { "key": "css3", "name": "CSS3", "type": "devicon" }, - { "key": "sass", "name": "SCSS", "type": "devicon" }, - { "key": "git", "name": "Git(Hub)", "type": "devicon" }, - { "key": "google", "name": "GA4/GTM/EEC", "type": "devicon" } - ] - }, - { - "project": "andrewnelson.net", - "url": "https://github.com/atlamors/portfolio", - "repo": "Public", - "descriptionTitle": "An open source portfolio", - "description": "built on Next.js and React. A fast and agile MERN stack single page application.", - "imageOptions": [ - { "key": "size", "value": "large" } - ], - "images": [ - { "key": "portfolio", "hover": "left", "h": "797", "w": "556", "url": "/img/portfolio-mock_single.png" } - ], - "stack": [ - { "key": "nextjs", "name": "Next.js", "type": "devicon" }, - { "key": "react", "name": "React", "type": "devicon" }, - { "key": "nodejs", "name": "Node.js", "type": "devicon" }, - { "key": "mongodb", "name": "MongoDB", "type": "devicon" }, - { "key": "javascript", "name": "JavaScript", "type": "devicon" }, - { "key": "html5", "name": "HTML5", "type": "devicon" }, - { "key": "css3", "name": "CSS3", "type": "devicon" }, - { "key": "sass", "name": "SCSS", "type": "devicon" }, - { "key": "git", "name": "Git(Hub)", "type": "devicon" } - ] - } -] \ No newline at end of file + { + "project": "LineupsValorant", + "type": "Gaming Platform", + "url": "https://lineupsvalorant.com", + "repo": "Private", + "descriptionTitle": "Platform for millions of Valorant players", + "description": "to discover and share the best agent lineups. Built with a focus on speed, accessibility, and community engagement.", + "stack": [ + { "key": "mysql", "name": "MySQL", "type": "devicon" }, + { "key": "javascript", "name": "JavaScript", "type": "devicon" }, + { "key": "python", "name": "Python", "type": "devicon" }, + { "key": "flask", "name": "Flask", "type": "devicon" }, + { "key": "google", "name": "Analytics", "type": "devicon" } + ] + }, + { + "project": "Robin", + "type": "AI Healthcare", + "url": "https://robin-ai.vercel.app", + "repo": "Private", + "descriptionTitle": "LLM-powered fraud detection", + "description": "for US government Medicaid and Medicare programs. Combines GPT-4, vector embeddings, and custom ML pipelines to identify suspicious patterns.", + "stack": [ + { "key": "nextjs", "name": "Next.js", "type": "devicon" }, + { "key": "python", "name": "Python", "type": "devicon" }, + { "key": "openai", "name": "GPT-4", "type": "custom" }, + { "key": "pinecone", "name": "Pinecone", "type": "custom" }, + { "key": "fastapi", "name": "FastAPI", "type": "devicon" } + ] + }, + { + "project": "NCut Visualization", + "type": "Research", + "url": null, + "repo": "Private", + "descriptionTitle": "Computer vision research", + "description": "on normalized cut algorithms and spectral clustering for image segmentation. Developed visualization tools to understand and improve segmentation quality.", + "stack": [ + { "key": "python", "name": "Python", "type": "devicon" }, + { "key": "pytorch", "name": "PyTorch", "type": "devicon" }, + { "key": "numpy", "name": "NumPy", "type": "devicon" }, + { "key": "matplotlib", "name": "Visualization", "type": "custom" } + ] + }, + { + "project": "Forte", + "type": "Music Tech", + "url": null, + "repo": "Private", + "descriptionTitle": "A cappella practice companion", + "description": "helping vocal groups rehearse more effectively. Features pitch tracking, harmony analysis, and section-specific practice modes.", + "stack": [ + { "key": "react", "name": "React", "type": "devicon" }, + { "key": "nodejs", "name": "Node.js", "type": "devicon" }, + { "key": "tensorflow", "name": "TensorFlow", "type": "custom" }, + { "key": "webAudio", "name": "Web Audio", "type": "custom" } + ] + } +] diff --git a/content/redesign/portfolio.json b/content/redesign/portfolio.json new file mode 100644 index 000000000..07a34ecf4 --- /dev/null +++ b/content/redesign/portfolio.json @@ -0,0 +1,49 @@ +{ + "header": { + "name": "Kyle Zhang", + "tagline": "Builder. Researcher. Creator.", + "description": "M&T graduate from UPenn, passionate about building products at the intersection of gaming, AI, and human impact.", + "email": "kyle@example.com", + "social": { + "github": "https://github.com/RiptideStar", + "linkedin": "https://www.linkedin.com/in/zhangkyle/", + "twitter": "https://twitter.com/" + } + }, + + "about": { + "intro": "I'm an entrepreneurial UPenn M&T graduate who loves turning ideas into impactful products. My journey spans from building gaming tools used by millions to developing AI-powered healthcare solutions. I believe in creating technology that genuinely helps people.", + "highlights": [ + { + "text": "Currently building AI solutions for fraud detection in ", + "link": { + "text": "government healthcare programs", + "url": "https://robin-ai.vercel.app" + }, + "suffix": "" + }, + { + "text": "Created ", + "link": { + "text": "LineupsValorant", + "url": "https://lineupsvalorant.com" + }, + "suffix": ", serving millions of players worldwide" + }, + { + "text": "Researched computer vision and ML at UPenn, contributing to NCut visualization and segmentation work", + "link": null, + "suffix": "" + }, + { + "text": "Passionate about a cappella, exploring new cities, and finding the best local food spots", + "link": null, + "suffix": "" + } + ] + }, + + "contact": { + "message": "I'm always excited to connect with fellow builders, researchers, and anyone passionate about technology. Whether you have a project idea, want to collaborate, or just want to chat about gaming, AI, or food—reach out!" + } +} diff --git a/node_modules/@fortawesome/.DS_Store b/node_modules/@fortawesome/.DS_Store new file mode 100644 index 000000000..e8be070b0 Binary files /dev/null and b/node_modules/@fortawesome/.DS_Store differ diff --git a/package-lock.json b/package-lock.json index 636c0ca70..086ac88dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "public_html", + "name": "portfolio", "lockfileVersion": 3, "requires": true, "packages": { @@ -24,11 +24,14 @@ "devicon": "^2.15.1", "framer-motion": "^8.5.0", "next": "^12.3", + "nodemailer": "^7.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.3.0", "react-intersection-observer": "^9.4.1", "react-markdown": "^8.0.4", "react-type-animation": "^2.1.2", + "resend": "^4.6.0", "swr": "^2.0.0", "the-new-css-reset": "^1.8.2", "uuid": "^9.0.0" @@ -2024,12 +2027,41 @@ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", "dev": true }, + "node_modules/@react-email/render": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.2.tgz", + "integrity": "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw==", + "dependencies": { + "html-to-text": "^9.0.5", + "prettier": "^3.5.3", + "react-promise-suspense": "^0.3.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", "dev": true }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", + "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/@sinclair/typebox": { "version": "0.25.21", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz", @@ -3623,10 +3655,9 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", - "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", - "dev": true, + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "engines": { "node": ">=0.10.0" } @@ -3746,6 +3777,30 @@ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -3758,6 +3813,33 @@ "node": ">=12" } }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.295", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz", @@ -3799,7 +3881,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -5336,6 +5417,21 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/html-to-text": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", + "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/html-void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", @@ -5345,6 +5441,24 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -8080,6 +8194,14 @@ "language-subtag-registry": "~0.3.2" } }, + "node_modules/leac": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", + "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -9147,6 +9269,14 @@ "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, + "node_modules/nodemailer": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz", + "integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -9465,6 +9595,18 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "node_modules/parseley": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", + "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "dependencies": { + "leac": "^0.6.0", + "peberminta": "^0.9.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9505,6 +9647,14 @@ "node": ">=8" } }, + "node_modules/peberminta": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", + "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -9627,6 +9777,20 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -9764,6 +9928,14 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-intersection-observer": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.1.tgz", @@ -9812,6 +9984,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/react-promise-suspense": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz", + "integrity": "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==", + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "node_modules/react-promise-suspense/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==" + }, "node_modules/react-type-animation": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/react-type-animation/-/react-type-animation-2.1.2.tgz", @@ -10071,6 +10256,17 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "node_modules/resend": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/resend/-/resend-4.6.0.tgz", + "integrity": "sha512-D5T2I82FvEUYFlrHzaDvVtr5ADHdhuoLaXgLFGABKyNtQgPWIuz0Vp2L2Evx779qjK37aF4kcw1yXJDHhA2JnQ==", + "dependencies": { + "@react-email/render": "1.1.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -10241,6 +10437,17 @@ "loose-envify": "^1.1.0" } }, + "node_modules/selderee": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", + "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "dependencies": { + "parseley": "^0.12.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", diff --git a/package.json b/package.json index 2f66a0b1c..395db20aa 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,14 @@ "devicon": "^2.15.1", "framer-motion": "^8.5.0", "next": "^12.3", + "nodemailer": "^7.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.3.0", "react-intersection-observer": "^9.4.1", "react-markdown": "^8.0.4", "react-type-animation": "^2.1.2", + "resend": "^4.6.0", "swr": "^2.0.0", "the-new-css-reset": "^1.8.2", "uuid": "^9.0.0" diff --git a/pages/_app.jsx b/pages/_app.jsx index c8fe40583..15274d9aa 100644 --- a/pages/_app.jsx +++ b/pages/_app.jsx @@ -2,12 +2,6 @@ import { Analytics } from '@vercel/analytics/react'; import { LazyMotion, domAnimation } from "framer-motion" -// Utils -import SetGridGap from '../components/utils/set.grid.util' - -// Structure -import Layout from '../components/layout/layout' - // CSS reset (https://github.com/elad2412/the-new-css-reset.git) import "../node_modules/the-new-css-reset/css/reset.css" @@ -15,15 +9,13 @@ import "../node_modules/the-new-css-reset/css/reset.css" import "@fontsource/fira-code/400.css" import "@fontsource/fira-code/600.css" import "@fontsource/inter/400.css" +import "@fontsource/inter/600.css" import "@fontsource/inter/700.css" import "@fontsource/inter/800.css" -// Devicon import (https://github.com/devicons/devicon) -import '../node_modules/devicon/devicon.min.css' - -// Global css -import '../styles/css/variables.css' -import '../styles/css/global.css' +// New redesign styles +import '../styles/css/variables-new.css' +import '../styles/css/global-new.css' /** * _app.jsx @@ -34,14 +26,9 @@ import '../styles/css/global.css' */ export default function MyApp({ Component, pageProps }) { return ( - <> - - - - - + + - ) -} \ No newline at end of file +} diff --git a/pages/api/contact.js b/pages/api/contact.js new file mode 100644 index 000000000..544170699 --- /dev/null +++ b/pages/api/contact.js @@ -0,0 +1,27 @@ +import { Resend } from 'resend'; + +export default async function handler(req, res) { + if (req.method !== 'POST') { + return res.status(405).json({ error: 'Method not allowed' }); + } + + const { name, email, message } = req.body; + if (!name || !email || !message) { + return res.status(400).json({ error: 'Missing required fields' }); + } + + try { + const resend = new Resend(process.env.RESEND_API_KEY); + const result = await resend.emails.send({ + from: 'onboarding@resend.dev', + to: 'kyle100@wharton.upenn.edu', // <-- replace with your Resend account email if different + subject: `${name} sent a message from Kyle's portfolio website`, + text: `Name: ${name}\nEmail: ${email}\nMessage: ${message}`, + }); + console.log('Resend API result:', result); + return res.status(200).json({ success: true, result }); + } catch (err) { + console.error('Resend API error:', err); + return res.status(500).json({ error: 'Failed to send email', details: err.message }); + } +} \ No newline at end of file diff --git a/pages/index.jsx b/pages/index.jsx index 335ec6ebe..5ea0eda20 100644 --- a/pages/index.jsx +++ b/pages/index.jsx @@ -1,26 +1,48 @@ -import Hero from '../components/sections/index/hero' -import Looking from '../components/sections/index/looking' -import About from '../components/sections/index/about' -import Technical from '../components/sections/index/technical' -import Career from '../components/sections/index/career' -import FeaturedProjects from '../components/sections/projects/featured' - -import Color from '../components/utils/page.colors.util' - -import colors from '../content/index/_colors.json' - -// -export default function HomePage() { - - return ( - <> - - - {/* */} - - - - {/* */} - - ); -} \ No newline at end of file +import Head from 'next/head'; +import Layout from '../components/redesign/Layout'; +import Header from '../components/redesign/Header'; +import About from '../components/redesign/sections/About'; +import Work from '../components/redesign/sections/Work'; +import Contact from '../components/redesign/sections/Contact'; + +import portfolioData from '../content/redesign/portfolio.json'; +import projectsData from '../content/projects/featured.json'; + +export default function HomePage() { + return ( + <> + + Kyle Zhang | Builder & Creator + + + + + {/* Open Graph */} + + + + + + {/* Twitter */} + + + + + + +
+ + + + + + + + + ); +} diff --git a/pages/links/index.jsx b/pages/links/index.jsx new file mode 100644 index 000000000..e7f1f37e1 --- /dev/null +++ b/pages/links/index.jsx @@ -0,0 +1,7 @@ +import ComingSoon from '../../components/sections/comingsoon'; + +export default function LinksPage() { + return ( + + ); +} diff --git a/public/.DS_Store b/public/.DS_Store new file mode 100644 index 000000000..44e86253c Binary files /dev/null and b/public/.DS_Store differ diff --git a/public/Profile_Exported.mov b/public/Profile_Exported.mov new file mode 100644 index 000000000..d8de89bc4 Binary files /dev/null and b/public/Profile_Exported.mov differ diff --git a/public/Profile_Exported_KyleText.mov b/public/Profile_Exported_KyleText.mov new file mode 100644 index 000000000..2f69fb9ff Binary files /dev/null and b/public/Profile_Exported_KyleText.mov differ diff --git a/public/favicon/android-chrome-192x192.png b/public/favicon/android-chrome-192x192.png index 98735c6e0..893883bee 100644 Binary files a/public/favicon/android-chrome-192x192.png and b/public/favicon/android-chrome-192x192.png differ diff --git a/public/favicon/android-chrome-384x384.png b/public/favicon/android-chrome-384x384.png deleted file mode 100644 index 779207b6f..000000000 Binary files a/public/favicon/android-chrome-384x384.png and /dev/null differ diff --git a/public/favicon/android-chrome-512x512.png b/public/favicon/android-chrome-512x512.png new file mode 100644 index 000000000..427827b01 Binary files /dev/null and b/public/favicon/android-chrome-512x512.png differ diff --git a/public/favicon/apple-touch-icon.png b/public/favicon/apple-touch-icon.png index fa65be871..bcb682e03 100644 Binary files a/public/favicon/apple-touch-icon.png and b/public/favicon/apple-touch-icon.png differ diff --git a/public/favicon/browserconfig.xml b/public/favicon/browserconfig.xml deleted file mode 100644 index 70cb989d3..000000000 --- a/public/favicon/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - #da532c - - - diff --git a/public/favicon/favicon-16x16.png b/public/favicon/favicon-16x16.png index 7fadcd019..1f4759358 100644 Binary files a/public/favicon/favicon-16x16.png and b/public/favicon/favicon-16x16.png differ diff --git a/public/favicon/favicon-32x32.png b/public/favicon/favicon-32x32.png index 76949cd69..697973972 100644 Binary files a/public/favicon/favicon-32x32.png and b/public/favicon/favicon-32x32.png differ diff --git a/public/favicon/favicon.ico b/public/favicon/favicon.ico index 0543754a9..8664b8e89 100644 Binary files a/public/favicon/favicon.ico and b/public/favicon/favicon.ico differ diff --git a/public/favicon/mstile-150x150.png b/public/favicon/mstile-150x150.png deleted file mode 100644 index c9034c7a7..000000000 Binary files a/public/favicon/mstile-150x150.png and /dev/null differ diff --git a/public/favicon/safari-pinned-tab.svg b/public/favicon/safari-pinned-tab.svg deleted file mode 100644 index 933efbb64..000000000 --- a/public/favicon/safari-pinned-tab.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - -Created by potrace 1.14, written by Peter Selinger 2001-2017 - - - - - diff --git a/public/favicon/site.webmanifest b/public/favicon/site.webmanifest index 4bae9f961..901bd0c6f 100644 --- a/public/favicon/site.webmanifest +++ b/public/favicon/site.webmanifest @@ -3,17 +3,17 @@ "short_name": "", "icons": [ { - "src": "/favicon/android-chrome-192x192.png", + "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "/favicon/android-chrome-384x384.png", - "sizes": "384x384", + "src": "/android-chrome-512x512.png", + "sizes": "512x512", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" -} +} \ No newline at end of file diff --git a/public/headshot.jpeg b/public/headshot.jpeg new file mode 100644 index 000000000..1b2ab2209 Binary files /dev/null and b/public/headshot.jpeg differ diff --git a/public/img/.DS_Store b/public/img/.DS_Store new file mode 100644 index 000000000..fda075ee1 Binary files /dev/null and b/public/img/.DS_Store differ diff --git a/public/img/Robin.png b/public/img/Robin.png new file mode 100644 index 000000000..5357494d6 Binary files /dev/null and b/public/img/Robin.png differ diff --git a/public/img/msc-mock_stack/left-phone-LineupsValorant-2.png b/public/img/msc-mock_stack/left-phone-LineupsValorant-2.png new file mode 100644 index 000000000..b0e10db9f Binary files /dev/null and b/public/img/msc-mock_stack/left-phone-LineupsValorant-2.png differ diff --git a/public/img/msc-mock_stack/left2-phone-LineupsValorant.png b/public/img/msc-mock_stack/left2-phone-LineupsValorant.png new file mode 100644 index 000000000..844bb6dfd Binary files /dev/null and b/public/img/msc-mock_stack/left2-phone-LineupsValorant.png differ diff --git a/public/img/msc-mock_stack/mac-LineupsValorant.png b/public/img/msc-mock_stack/mac-LineupsValorant.png new file mode 100644 index 000000000..1cdd414fe Binary files /dev/null and b/public/img/msc-mock_stack/mac-LineupsValorant.png differ diff --git a/public/img/msc-mock_stack/mac2-LineupsValorant.png b/public/img/msc-mock_stack/mac2-LineupsValorant.png new file mode 100644 index 000000000..0ab8fb445 Binary files /dev/null and b/public/img/msc-mock_stack/mac2-LineupsValorant.png differ diff --git a/styles/css/global-new.css b/styles/css/global-new.css new file mode 100644 index 000000000..56a65cfb2 --- /dev/null +++ b/styles/css/global-new.css @@ -0,0 +1,164 @@ +/* Global Styles - Minimalist Portfolio */ + +* { + box-sizing: border-box; +} + +::selection { + background: var(--text-primary); + color: var(--bg-primary); +} + +html { + font-size: 16px; + scroll-behavior: smooth; +} + +body { + font-family: var(--font-sans); + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.7; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#__next { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* Typography */ +h1, h2, h3, h4, h5, h6 { + font-weight: 600; + line-height: 1.3; + color: var(--text-primary); + margin: 0; +} + +h1 { + font-size: var(--text-4xl); + font-weight: 500; + letter-spacing: -0.02em; +} + +h2 { + font-size: var(--text-2xl); + font-weight: 600; + letter-spacing: -0.01em; +} + +h3 { + font-size: var(--text-xl); + font-weight: 600; +} + +p { + color: var(--text-secondary); + margin: 0; +} + +/* Links */ +a { + color: var(--link-color); + text-decoration: none; + transition: color var(--transition-fast); +} + +a:hover { + color: var(--link-hover); +} + +/* Lists */ +ul, ol { + padding-left: var(--space-6); + margin: 0; +} + +ul li, ol li { + color: var(--text-secondary); + margin-bottom: var(--space-2); +} + +ul li::marker, ol li::marker { + color: var(--text-muted); +} + +/* Buttons */ +button { + cursor: pointer; + font-family: inherit; + border: none; + background: none; +} + +/* Images */ +img { + max-width: 100%; + height: auto; +} + +/* Focus styles for accessibility */ +:focus-visible { + outline: 2px solid var(--link-color); + outline-offset: 2px; +} + +/* Utility classes */ +.mono { + font-family: var(--font-mono); +} + +.text-muted { + color: var(--text-muted); +} + +.text-secondary { + color: var(--text-secondary); +} + +/* Smooth page transitions */ +.page-transition-enter { + opacity: 0; + transform: translateY(10px); +} + +.page-transition-enter-active { + opacity: 1; + transform: translateY(0); + transition: opacity 300ms ease, transform 300ms ease; +} + +.page-transition-exit { + opacity: 1; +} + +.page-transition-exit-active { + opacity: 0; + transition: opacity 200ms ease; +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--bg-secondary); +} + +::-webkit-scrollbar-thumb { + background: var(--border-color); + border-radius: var(--radius-full); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--border-hover); +} + +/* Main content area */ +main { + flex: 1; +} diff --git a/styles/css/global.css b/styles/css/global.css deleted file mode 100644 index d3296a0b9..000000000 --- a/styles/css/global.css +++ /dev/null @@ -1 +0,0 @@ -::-moz-selection{color:var(--background);background:var(--secondary)}::selection{color:var(--background);background:var(--secondary)}html{font-size:16px;font-family:var(--font-sans);color:var(--primary-dim);background:var(--background);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}#__next{width:100%;overflow-x:hidden}img{border-radius:.5rem;filter:brightness(var(--brightness-low))}img:hover{filter:brightness(var(--brightness-high))}button.button{cursor:pointer;font-family:var(--font-accent);font-size:1rem;font-weight:700;padding:.75rem 2.5rem;border-radius:99rem;border:none;margin-right:1rem;transition:all,cubic-bezier(0.4, 0, 0.2, 1),750ms}button.button:hover{transition:all,cubic-bezier(0.4, 0, 0.2, 1),750ms}h1{font-size:var(--font-xl);font-family:var(--font-sans);font-weight:600;letter-spacing:-0.1rem}h2{color:var(--primary);font-size:var(--font-xl);font-family:var(--font-sans);font-weight:700;letter-spacing:-0.05rem}h3{font-size:var(--font-m);font-family:var(--font-sans);color:var(--primary);font-weight:600;letter-spacing:-0.01rem}h4{font-size:var(--font-xs);font-family:var(--font-sans);font-weight:600;text-transform:uppercase;letter-spacing:.1rem}h5{font-size:var(--font-m);font-family:var(--font-sans);color:var(--primary);font-weight:600;letter-spacing:-0.01rem}p{font-size:var(--font-r);font-family:var(--font-sans);line-height:1.6;letter-spacing:-0.02rem}p.subtitle{font-family:var(--font-accent);font-size:var(--font-r-s);font-weight:600;letter-spacing:0}a{font-weight:600;color:var(--primary)}a:hover{color:var(--primary-bright)}a:hover [data-prefix=far][data-icon=arrow-up-right-from-square]{color:var(--primary)}a:hover h3{color:var(--primary-bright)}a [data-prefix=far][data-icon=arrow-up-right-from-square]{height:.75rem;vertical-align:-1.5px;color:var(--primary-dim)}a svg{margin-left:.25rem}.noEvents{pointer-events:none}.list{list-style:disc;margin-left:1.5rem}.list li::marker{content:normal}.leaveSite:after{content:"↗";position:relative;font-size:inherit;line-height:0;vertical-align:-1px;margin-left:.5rem;text-decoration:none;text-rendering:optimizeLegibility;white-space:nowrap;font-feature-settings:"liga";-webkit-font-smoothing:antialiased}.borderBottom{border-bottom:1px solid var(--primary-dark)}#gradient-canvas{position:absolute;z-index:-1;opacity:100%;left:0;top:0;width:100%;height:100%;transform:rotate(180deg);aspect-ratio:auto;--gradient-color-1: var( --mesh-color-1 );--gradient-color-2: var( --mesh-color-2 );--gradient-color-3: var( --mesh-color-3 );--gradient-color-4: var( --mesh-color-4 )}.highlight{--looking-bg-1: var(--neon-1-1);--looking-bg-2: var(--neon-1-2);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;background:var(--bar-gradient);--bar-gradient: linear-gradient( 90deg, var(--looking-bg-1) 0%, var(--looking-bg-2) 100% );-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:rgba(0,0,0,0)}/*# sourceMappingURL=global.css.map */ \ No newline at end of file diff --git a/styles/css/global.css.map b/styles/css/global.css.map deleted file mode 100644 index d79f07c20..000000000 --- a/styles/css/global.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["global.css","../scss/global.scss","../scss/_mixins.scss"],"names":[],"mappings":"AAAA,iBCGA,uBACC,CAAA,2BACA,CDLD,YCGA,uBACC,CAAA,2BACA,CAAA,KAGD,cACC,CAAA,4BACA,CAAA,wBAEA,CAAA,4BACA,CAAA,iCAEA,CAAA,kCACG,CAAA,QAGJ,UACC,CAAA,iBACG,CAAA,IAGJ,mBACC,CAAA,wCAEA,CAAA,UAEA,yCACC,CAAA,cAIF,cACC,CAAA,8BACA,CAAA,cACA,CAAA,eACA,CAAA,qBACA,CAAA,mBACA,CAAA,WACA,CAAA,iBACA,CAAA,iDCpBA,CAAA,oBDuBA,iDCvBA,CAAA,GD4BD,wBACC,CAAA,4BACA,CAAA,eACA,CAAA,sBACA,CAAA,GAGD,oBACC,CAAA,wBACA,CAAA,4BACA,CAAA,eACA,CAAA,uBACG,CAAA,GAGJ,uBACC,CAAA,4BACA,CAAA,oBACA,CAAA,eACA,CAAA,uBACA,CAAA,GAGD,wBACC,CAAA,4BACA,CAAA,eACA,CAAA,wBACA,CAAA,oBACA,CAAA,GAGD,uBACC,CAAA,4BACA,CAAA,oBACA,CAAA,eACA,CAAA,uBACA,CAAA,EAGD,uBACC,CAAA,4BACA,CAAA,eACA,CAAA,uBACA,CAAA,WAEA,8BACC,CAAA,yBACA,CAAA,eACA,CAAA,gBACA,CAAA,EAIF,eACC,CAAA,oBACA,CAAA,QAEA,2BACC,CAAA,gEAEA,oBACC,CAAA,WAGD,2BACC,CAAA,0DAMF,aACC,CAAA,qBACA,CAAA,wBACA,CAAA,MAGD,kBACC,CAAA,UAIF,mBACC,CAAA,MAGD,eACC,CAAA,kBACA,CAAA,iBAEC,cACC,CAAA,iBAMF,WACC,CAAA,iBACA,CAAA,iBACA,CAAA,aACA,CAAA,mBACA,CAAA,iBACA,CAAA,oBACA,CAAA,iCACA,CAAA,kBACA,CAAA,4BACA,CAAA,kCACA,CAAA,cAIF,2CACC,CAAA,iBAGD,iBACC,CAAA,UACG,CAAA,YACH,CAAA,MACG,CAAA,KACA,CAAA,UACA,CAAA,WACA,CAAA,wBACA,CAAA,iBACA,CAAA,yCACA,CAAA,yCACH,CAAA,yCACA,CAAA,yCACA,CAAA,WAGD,+BACC,CAAA,+BACA,CAAA,yBAEA,CAFA,sBAEA,CAFA,iBAEA,CAAA,8BACA,CAAA,0FACA,CAAA,4BAOA,CAAA,oBACA,CAAA,qCACA","file":"global.css"} \ No newline at end of file diff --git a/styles/css/variables-new.css b/styles/css/variables-new.css new file mode 100644 index 000000000..6992f4956 --- /dev/null +++ b/styles/css/variables-new.css @@ -0,0 +1,129 @@ +/* Portfolio Design System - Minimalist & Clean */ + +/* Fonts - Clean, modern typography */ +:root { + --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif; + --font-mono: "Fira Code", "SF Mono", "Monaco", "Inconsolata", monospace; + --font-serif: "Georgia", "Times New Roman", serif; + + /* Font sizes - refined scale */ + --text-xs: 0.75rem; + --text-sm: 0.875rem; + --text-base: 1rem; + --text-lg: 1.125rem; + --text-xl: 1.25rem; + --text-2xl: 1.5rem; + --text-3xl: 2rem; + --text-4xl: 2.75rem; + --text-5xl: 3.5rem; + + /* Spacing scale */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.25rem; + --space-6: 1.5rem; + --space-8: 2rem; + --space-10: 2.5rem; + --space-12: 3rem; + --space-16: 4rem; + --space-20: 5rem; + --space-24: 6rem; + --space-32: 8rem; + + /* Layout */ + --max-width: 800px; + --max-width-narrow: 640px; + --mobile: 640px; + + /* Border radius */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-full: 9999px; + + /* Transitions */ + --transition-fast: 150ms ease; + --transition-base: 200ms ease; + --transition-slow: 300ms ease; + --transition-slower: 500ms ease; + + /* Dot matrix colors */ + --dot-inactive: #e5e7eb; +} + +/* Light theme (default) */ +:root, +:root[data-theme='light'] { + --bg-primary: #fafafa; + --bg-secondary: #ffffff; + --bg-tertiary: #f5f5f5; + --bg-card: #ffffff; + + --border-color: #e5e5e5; + --border-hover: #d4d4d4; + --border-strong: #a3a3a3; + + --text-primary: #0a0a0a; + --text-secondary: #404040; + --text-muted: #737373; + --text-faint: #a3a3a3; + + --accent: #0a0a0a; + --accent-hover: #262626; + --accent-muted: #525252; + + --link-color: #2563eb; + --link-hover: #1d4ed8; + + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.07); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08); + + --dot-inactive: #d1d5db; + + color-scheme: light; +} + +/* Dark theme */ +:root[data-theme='dark'] { + --bg-primary: #0a0a0a; + --bg-secondary: #141414; + --bg-tertiary: #1a1a1a; + --bg-card: #141414; + + --border-color: #262626; + --border-hover: #404040; + --border-strong: #525252; + + --text-primary: #fafafa; + --text-secondary: #a3a3a3; + --text-muted: #737373; + --text-faint: #525252; + + --accent: #fafafa; + --accent-hover: #e5e5e5; + --accent-muted: #a3a3a3; + + --link-color: #60a5fa; + --link-hover: #93c5fd; + + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4); + + --dot-inactive: #374151; + + color-scheme: dark; +} + +/* Responsive font sizes */ +@media (max-width: 640px) { + :root { + --text-3xl: 1.75rem; + --text-4xl: 2.25rem; + --text-5xl: 2.75rem; + } +} diff --git a/styles/css/variables.css b/styles/css/variables.css deleted file mode 100644 index 60165df8b..000000000 --- a/styles/css/variables.css +++ /dev/null @@ -1 +0,0 @@ -:root{--font-sans: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;--font-accent: "Fira Code", monospace}:root{--mobile: 768px;--reading-width: 1000px}:root{--font-xx: 0.60rem;--font-xs: 0.75rem;--font-s: 0.875rem;--font-r-s: 1.00rem;--font-r: 1.00rem;--font-m: 1.125rem;--font-l: 2.00rem;--font-xl-l: 3.75rem;--font-xl: 3.75rem;--size-4-: 0.25rem;--size-3-: 0.35rem;--size-2-: 0.50rem;--size-1x: 1.00rem;--size-2x: 2.00rem;--size-3x: 3.00rem;--size-4x: 4.00rem;--size-5x: 5.00rem;--size-6x: 7.00rem;--size-7x: 10.00rem}@media screen and (max-width: 767px){:root{--font-xx: 0.60rem;--font-xs: 0.75rem;--font-s: 0.875rem;--font-r-s: 0.875rem;--font-r: 0.95rem;--font-m: 1.00rem;--font-l: 2.00rem;--font-xl-l: 2.00rem;--font-xl: 2.75rem;--size-4-: 0.25rem;--size-3-: 0.35rem;--size-2-: 0.50rem;--size-1x: 1.00rem;--size-2x: 1.50rem;--size-3x: 2.00rem;--size-4x: 3.00rem;--size-5x: 4.00rem;--size-6x: 5.50rem;--size-7x: 7.00rem}}:root[data-theme=dark]{--primary-bright: #ffffff;--primary: #bfbfbf;--primary-dim: #888889;--primary-dim2: #262627;--primary-dark: #141415;--secondary: #7feaff;--secondary-bright: #5f8cff;--background: #060708;--background-dim: #08090a;--background-dim2: #0e0f11;--neon-1-1: #a7d575;--neon-1-2: #52abc4;--neon-2-1: #37eaf7;--neon-2-2: #c624ee;--icon-bright: #ffffff;--brightness-low: 100%;--brightness-high: 100%;--mesh-color-1: #060708;--mesh-color-2: #030408;--mesh-color-3: #05070a;--mesh-color-4: #0c1019;color-scheme:dark}:root[data-theme=light],:root[data-theme=undefined]{--primary-bright: #000000;--primary: #2e2e2e;--primary-dim: #3a3a3a;--primary-dim2: #585858;--primary-dark: #dfdfdf;--secondary: #002aff;--secondary-bright: #4800ff;--background: #ededed;--background-dim: #fbfbfb;--background-dim2: #fefefe;--neon-1-1: #424bff;--neon-1-2: #2493ee;--neon-2-1: #37eaf7;--neon-2-2: #c624ee;--icon-bright: var(--neon-1-1);--mesh-color-1: #96c4f3;--mesh-color-2: #96a8f2;--mesh-color-3: #bad0ef;--mesh-color-4: #c8ceda}:root[data-theme=unicorn]{--primary-bright: #000000;--primary: #2e2e2e;--primary-dim: #464646;--primary-dim2: #585858;--primary-dark: #dfdfdf;--secondary: #ff00bb;--secondary-bright: #feff00;--background: #ffffff;--background-dim: #fbfbfb;--background-dim2: #fefefe;--neon-1-1: #37eaf7;--neon-1-2: #c624ee;--neon-2-1: #eaf737;--neon-2-2: #2493ee;--icon-bright: var(--neon-1-1);--mesh-color-1: #96c4f3;--mesh-color-2: #f296cc;--mesh-color-3: #efe2ba;--mesh-color-4: #c8d7da}/*# sourceMappingURL=variables.css.map */ \ No newline at end of file diff --git a/styles/css/variables.css.map b/styles/css/variables.css.map deleted file mode 100644 index 82a5c1ddb..000000000 --- a/styles/css/variables.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../scss/variables.scss","../scss/_mixins.scss"],"names":[],"mappings":"AAGA,MACC,4JAAA,CACA,qCAAA,CAID,MACC,eAAA,CACA,uBAAA,CAID,MAEC,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,mBAAA,CACA,iBAAA,CACA,kBAAA,CACA,iBAAA,CACA,oBAAA,CACA,kBAAA,CAGA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,mBAAA,CC3BE,qCDgCF,MAEC,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,oBAAA,CACA,iBAAA,CACA,iBAAA,CACA,iBAAA,CACA,oBAAA,CACA,kBAAA,CAGA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CACA,kBAAA,CAAA,CAKF,uBACC,yBAAA,CACA,kBAAA,CACA,sBAAA,CACA,uBAAA,CACA,uBAAA,CAEA,oBAAA,CACA,2BAAA,CAEA,qBAAA,CACA,yBAAA,CACA,0BAAA,CAEA,mBAAA,CACA,mBAAA,CACA,mBAAA,CACA,mBAAA,CAEA,sBAAA,CAEA,sBAAA,CACA,uBAAA,CAEA,uBAAA,CACG,uBAAA,CACA,uBAAA,CACA,uBAAA,CAEH,iBAAA,CAID,oDAEC,yBAAA,CACA,kBAAA,CACA,sBAAA,CACA,uBAAA,CACA,uBAAA,CAEA,oBAAA,CACA,2BAAA,CAEA,qBAAA,CACA,yBAAA,CACA,0BAAA,CAEA,mBAAA,CACA,mBAAA,CACA,mBAAA,CACA,mBAAA,CAEA,8BAAA,CAEA,uBAAA,CACG,uBAAA,CACA,uBAAA,CACA,uBAAA,CAIJ,0BACC,yBAAA,CACA,kBAAA,CACA,sBAAA,CACA,uBAAA,CACA,uBAAA,CAEA,oBAAA,CACA,2BAAA,CAEA,qBAAA,CACA,yBAAA,CACA,0BAAA,CAEA,mBAAA,CACA,mBAAA,CACA,mBAAA,CACA,mBAAA,CAEA,8BAAA,CAEA,uBAAA,CACG,uBAAA,CACA,uBAAA,CACA,uBAAA","file":"variables.css"} \ No newline at end of file diff --git a/styles/scss/global.scss b/styles/scss/global.scss deleted file mode 100644 index 9c5122ebf..000000000 --- a/styles/scss/global.scss +++ /dev/null @@ -1,199 +0,0 @@ -@import '_variables'; -@import '_mixins'; - -::selection { - color: var(--background); - background: var(--secondary); -} - -html { - font-size: 16px; - font-family: var(--font-sans); - - color: var(--primary-dim); - background: var(--background); - - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; -} - -#__next { - width: 100%; - overflow-x: hidden; -} - -img { - border-radius: .5rem; - //width: 100%; - filter: brightness( var(--brightness-low) ); - - &:hover { - filter: brightness( var(--brightness-high) ); - } -} - -button.button { - cursor: pointer; - font-family: var(--font-accent); - font-size: 1rem; - font-weight: 700; - padding: .75rem 2.5rem; - border-radius: 99rem; - border: none; - margin-right: 1rem; - - @include transition(750ms); - &:hover { - @include transition(750ms); - } -} - -h1 { - font-size: var(--font-xl); - font-family: var(--font-sans); - font-weight: 600; - letter-spacing: -.1rem; -} - -h2 { - color: var(--primary); - font-size: var(--font-xl); - font-family: var(--font-sans); - font-weight: 700; - letter-spacing: -.05rem; -} - -h3 { - font-size: var(--font-m); - font-family: var(--font-sans); - color: var(--primary); - font-weight: 600; - letter-spacing: -.01rem; -} - -h4 { - font-size: var(--font-xs); - font-family: var(--font-sans); - font-weight: 600; - text-transform: uppercase; - letter-spacing: .1rem; -} - -h5 { - font-size: var(--font-m); - font-family: var(--font-sans); - color: var(--primary); - font-weight: 600; - letter-spacing: -.01rem; -} - -p { - font-size: var(--font-r); - font-family: var(--font-sans); - line-height: 1.6; - letter-spacing: -.02rem; - - &.subtitle { - font-family: var(--font-accent); - font-size: var(--font-r-s); - font-weight: 600; - letter-spacing: 0; - } -} - -a { - font-weight: 600; - color: var(--primary); - - &:hover { - color: var(--primary-bright); - - [data-prefix="far"][data-icon="arrow-up-right-from-square"] { - color: var(--primary); - } - - h3 { - color: var(--primary-bright); - } - } - - - - [data-prefix="far"][data-icon="arrow-up-right-from-square"] { - height: .75rem; - vertical-align: -1.5px; - color: var(--primary-dim); - } - - svg { - margin-left: .25rem; - } -} - -.noEvents { - pointer-events: none; -} - -.list { - list-style: disc; - margin-left: 1.5rem; - li { - &::marker { - content: normal; - } - } -} - -.leaveSite { - &:after { - content: "↗"; - position: relative; - font-size: inherit; - line-height: 0; - vertical-align: -1px; - margin-left: .5rem; - text-decoration: none; - text-rendering: optimizeLegibility; - white-space: nowrap; - font-feature-settings: "liga"; - -webkit-font-smoothing: antialiased; - } -} - -.borderBottom { - border-bottom: 1px solid var(--primary-dark); -} - -#gradient-canvas { - position: absolute; - z-index: -1; - opacity: 100%; - left: 0; - top: 0; - width: 100%; - height: 100%; - transform: rotate(180deg); - aspect-ratio: auto; - --gradient-color-1: var( --mesh-color-1 ); - --gradient-color-2: var( --mesh-color-2 ); - --gradient-color-3: var( --mesh-color-3 ); - --gradient-color-4: var( --mesh-color-4 ); -} - -.highlight { - --looking-bg-1: var(--neon-1-1); - --looking-bg-2: var(--neon-1-2); - - width: fit-content; - background: var(--bar-gradient); - --bar-gradient: - linear-gradient( - 90deg, - var(--looking-bg-1) 0%, - var(--looking-bg-2) 100% - ); - - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; -} \ No newline at end of file diff --git a/styles/scss/variables.scss b/styles/scss/variables.scss deleted file mode 100644 index aa27ddf22..000000000 --- a/styles/scss/variables.scss +++ /dev/null @@ -1,159 +0,0 @@ -@import "../scss/mixins"; - -// Fonts -:root { - --font-sans: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif; - --font-accent: "Fira Code", monospace; -} - -// Breakpoints and containers -:root { - --mobile: 768px; - --reading-width: 1000px; -} - -// Spatial sizing -:root { - // Fonts sizes - --font-xx: 0.60rem; - --font-xs: 0.75rem; - --font-s: 0.875rem; - --font-r-s: 1.00rem; - --font-r: 1.00rem; - --font-m: 1.125rem; - --font-l: 2.00rem; - --font-xl-l: 3.75rem; - --font-xl: 3.75rem; - - // Marging/padding sizes - --size-4-: 0.25rem; - --size-3-: 0.35rem; - --size-2-: 0.50rem; - --size-1x: 1.00rem; - --size-2x: 2.00rem; - --size-3x: 3.00rem; - --size-4x: 4.00rem; - --size-5x: 5.00rem; - --size-6x: 7.00rem; - --size-7x: 10.00rem; -} - -// Spatial sizing -@include media($max: $mobile) { - :root { - // Fonts sizes - --font-xx: 0.60rem; - --font-xs: 0.75rem; - --font-s: 0.875rem; - --font-r-s: 0.875rem; - --font-r: 0.95rem; - --font-m: 1.00rem; - --font-l: 2.00rem; - --font-xl-l: 2.00rem; - --font-xl: 2.75rem; - - // Marging/padding sizes - --size-4-: 0.25rem; - --size-3-: 0.35rem; - --size-2-: 0.50rem; - --size-1x: 1.00rem; - --size-2x: 1.50rem; - --size-3x: 2.00rem; - --size-4x: 3.00rem; - --size-5x: 4.00rem; - --size-6x: 5.50rem; - --size-7x: 7.00rem; - } -} - -// Dark mode theme -:root[data-theme='dark'] { - --primary-bright: #ffffff; - --primary: #bfbfbf; - --primary-dim: #888889; - --primary-dim2: #262627; - --primary-dark: #141415; - - --secondary: #7feaff; - --secondary-bright: #5f8cff; - - --background: #060708; - --background-dim: #08090a; - --background-dim2: #0e0f11; - - --neon-1-1: #a7d575; - --neon-1-2: #52abc4; - --neon-2-1: #37eaf7; - --neon-2-2: #c624ee; - - --icon-bright: #ffffff; - - --brightness-low: 100%; - --brightness-high: 100%; - - --mesh-color-1: #060708; - --mesh-color-2: #030408; - --mesh-color-3: #05070a; - --mesh-color-4: #0c1019; - - color-scheme: dark; -} - -// Light mode theme -:root[data-theme='light'], -:root[data-theme='undefined'] { - --primary-bright: #000000; - --primary: #2e2e2e; - --primary-dim: #3a3a3a; - --primary-dim2: #585858; - --primary-dark: #dfdfdf; - - --secondary: #002aff; - --secondary-bright: #4800ff; - - --background: #ededed; - --background-dim: #fbfbfb; - --background-dim2: #fefefe; - - --neon-1-1: #424bff; - --neon-1-2: #2493ee; - --neon-2-1: #37eaf7; - --neon-2-2: #c624ee; - - --icon-bright: var(--neon-1-1); - - --mesh-color-1: #96c4f3; - --mesh-color-2: #96a8f2; - --mesh-color-3: #bad0ef; - --mesh-color-4: #c8ceda; -} - -// Unicorn mode theme -:root[data-theme='unicorn'] { - --primary-bright: #000000; - --primary: #2e2e2e; - --primary-dim: #464646; - --primary-dim2: #585858; - --primary-dark: #dfdfdf; - - --secondary: #ff00bb; - --secondary-bright: #feff00; - - --background: #ffffff; - --background-dim: #fbfbfb; - --background-dim2: #fefefe; - - --neon-1-1: #37eaf7; - --neon-1-2: #c624ee; - --neon-2-1: #eaf737; - --neon-2-2: #2493ee; - - --icon-bright: var(--neon-1-1); - - --mesh-color-1: #96c4f3; - --mesh-color-2: #f296cc; - --mesh-color-3: #efe2ba; - --mesh-color-4: #c8d7da; -} - - diff --git a/styles/sections/index/about.module.scss b/styles/sections/index/about.module.scss deleted file mode 100644 index e6e8bdab9..000000000 --- a/styles/sections/index/about.module.scss +++ /dev/null @@ -1,157 +0,0 @@ -@import '../../scss/mixins'; - -// Section -.section { - - .content { - $padding: 2rem; - position: relative; - display: flex; - flex-direction: row; - gap: 1rem; - - > div { - padding: 0; - } - - h3 { - position: relative; - color: var(--primary); - - // &:before { - // content: ''; - // position: absolute; - // left: calc( $padding * -1 ); - // width: 18px; - // aspect-ratio: 1/1; - // background-color: var(--primary-bright); - // border-radius: 99rem; - // border: 3px solid var(--background); - // top: 50%; - // transform: translate(-50%, -50%); - // } - - // &:after { - // content: ''; - // position: absolute; - // left: calc( $padding * -1 ); - // width: 9px; - // aspect-ratio: 1/1; - // background-color: var(--background); - // border-radius: 99rem; - // top: 50%; - // transform: translate(-50%, -50%); - // } - } - - .container { - display: flex; - flex-direction: column; - gap: var(--size-1x); - border: 1px solid var(--primary-dark); - border-radius: 2rem; - background: var(--background-dim2); - box-shadow: 0 0 30px var(--background); - overflow: hidden; - - padding: var(--padding); - --padding: 2rem; - --padding-left: var(--padding); - --padding-top: var(--padding); - --padding-right: var(--padding); - --padding-bottom: var(--padding); - - &:not(:last-of-type) { - margin-bottom: var(--size-1x); - } - } - - .copy { - flex-basis: calc(60% + 1px); - flex-grow: 1; - flex-shrink: 1; - } - - .icon { - padding: 0.5rem; - display: flex; - align-items: center; - background: var(--primary-dark); - width: fit-content; - border-radius: 0.5rem; - aspect-ratio: 1; - - svg { - height: 1.6rem; - color: var(--primary); - - path { - fill: url(#fa-gradient) #fff; - } - } - - - } - - // Columns - .image { - flex-basis: 50%; - flex-grow: 1; - flex-shrink: 1; - position: relative; - - img { - height: 100%; - width: auto; - aspect-ratio: 3/4; - object-fit: cover; - border-radius: 2rem; - } - - &.technicalSvg { - > span { - position: relative !important; - overflow: visible !important; - height: 100% !important; - z-index: -1; - } - img { - overflow: visible; - filter: brightness(100%); - } - } - } - } -} - -@include media($max: $mobile) { - .section { - .content { - flex-direction: column; - - > div { - flex-basis: 100% !important; - } - - .container { - padding: 2rem 1.25rem; - } - - .image { - padding: 1rem; - &.technicalSvg { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - height: 140%; - z-index: -1; - } - } - - .copy { - padding: 0; - } - } - } -} \ No newline at end of file diff --git a/styles/sections/index/career.module.scss b/styles/sections/index/career.module.scss deleted file mode 100644 index 427c09fe1..000000000 --- a/styles/sections/index/career.module.scss +++ /dev/null @@ -1,154 +0,0 @@ -// Career component -@import '../../scss/mixins'; - -.section { - - .area { - display: flex; - flex-direction: column; - gap: 1rem; - - h3 { - margin-top: - .25rem; - margin-bottom: .35rem; - } - - h4 { - font-size: .9rem; - margin-bottom: .25rem; - text-transform: none; - letter-spacing: 0; - } - - h5 { - font-size: .9rem; - margin-bottom: .25rem; - opacity: .5; - } - } - - .company { - position: relative; - display: flex; - flex-direction: row; - gap: 1rem; - } - - .company, - .position { - border: 1px solid var(--primary-dark); - border-radius: 1rem; - overflow: hidden; - - padding: var(--padding); - --padding: 2rem; - --padding-left: var(--padding); - --padding-top: var(--padding); - --padding-right: var(--padding); - --padding-bottom: var(--padding); - - background: var(--background-dim2); - } - - .companyContent { - display: flex; - flex-direction: column; - gap: 1rem; - max-width: calc( var(--grid-32) * 20 ); - } - - .companyPositions { - display: flex; - flex-direction: column; - gap: 1rem; - } - - .position { - margin-left: calc( var(--grid-32) * 2 ); - background: var(--background-dim); - max-width: 100%; - - &.first { - .content { - padding-bottom: 0; - } - } - - .positionContent { - position: relative; - display: flex; - flex-direction: column; - gap: 2rem; - - .list { - display: flex; - flex-direction: column; - gap: .75rem; - - .subList { - position: relative; - margin-left: 2rem; - margin-top: .5rem; - display: block; - color: var(--primary); - - .bullet { - position: absolute; - margin-left: -1.5rem; - &:before { - content: "⇒"; - } - } - } - - } - } - } - - .technicalSvg { - position: absolute; - top: 0; - bottom: 0; - right: 0; - - img { - transform: rotate(90deg); - } - } -} - -@include media($max: $mobile) { - .section { - .area { - .company, - .position { - padding: 2rem 1rem; - } - .position { - padding-bottom: 1rem; - --padding-top: 1rem; - } - .companyContent { - max-width: 100%; - } - .positionContent { - gap: 1rem; - max-width: 100%; - } - p { - font-size: .8rem; - } - ul.list { - li { - font-size: .8rem; - } - } - - ul:not(.list) { - li { - font-size: .6rem; - } - } - } - } -} \ No newline at end of file diff --git a/styles/sections/index/hero.module.scss b/styles/sections/index/hero.module.scss deleted file mode 100644 index af0956756..000000000 --- a/styles/sections/index/hero.module.scss +++ /dev/null @@ -1,173 +0,0 @@ -@import '../../scss/_mixins'; - -//global.module.scss -.section { - border-bottom: 1px solid var(--primary-dark); - - max-height: 1440px; - height: 100vh; - justify-content: center; - - > .container { - height: fit-content; - } - - .preHeader { - color: var(--secondary); - font-family: var(--font-accent); - font-size: 1.2rem; - font-weight: 400; - margin: 1rem 0; - } - - .header { - color: var(--primary-bright); - font-family: var(--font-sans); - font-size: 5rem; - margin: 0; - } - - .primaryDim { - color: var(--primary-dim); - } - - .primaryBright { - color: var(--primary); - } - - .tempBright { - font-size: .875rem; - width: 60%; - } - - .gitBadges { - position: relative; - display: flex; - gap: .5rem; - flex-wrap: wrap; - height: 1rem; - img { - height: 16px; - width: fit-content; - border-radius: .25rem; - } - } - - @include media($max: $mobile) { - .preHeader { - font-size: 1rem; - } - .header { - font-size: 2.9rem; - } - .tempBright { - width: 100%; - padding-right: 1rem; - } - button { - font-size: .875rem; - padding: 0.75rem 2rem; - } - } -} - -// Backgrounds -.colorfulV1 { - --hero-gradient-bg-1: var(--neon-1-1); - --hero-gradient-bg-2: var(--neon-1-2); - --hero-gradient-bg-3: var(--neon-2-1); - --hero-gradient-bg-4: var(--neon-2-2); - - position: absolute; - pointer-events: none; - top: 0; - right: 0; - bottom: 0; - left: 0; - - .svg_background { - position: absolute; - z-index: -1; - width: 200vw; - aspect-ratio: 1/1; - bottom: 50%; - right: 50%; - opacity: 15%; - transform: translate(48.5%, 50%); - max-inline-size: unset; - max-block-size: unset; - } - - .radialContainer { - position: absolute; - width: 100%; - height: 100%; - - .radialGradient { - position: absolute; - right: -30%; - width: 100%; - left: unset; - aspect-ratio: 1/1; - height: unset; - bottom: -60%; - filter: blur(100px); - transform: translateZ(0); - opacity: 20%; - background: var(--bar-gradient); - --bar-gradient: radial-gradient( - circle, - var(--hero-gradient-bg-1) 0%, - var(--hero-gradient-bg-2) 24%, - var(--hero-gradient-bg-3) 44%, - var(--hero-gradient-bg-4) 57%, - transparent 70% - ); - } - } - - .barContainer { - position: absolute; - left: 0; - bottom: 0; - width: 100%; - height: 100px; - } - - .barGradient { - --bar-opacity:0.15; - --bar-blend-mode:screen; - --bar-gradient: linear-gradient( - 90deg, - var(--hero-gradient-bg-1) 0%, - var(--hero-gradient-bg-2) 26.88%, - var(--hero-gradient-bg-3) 48.46%, - var(--hero-gradient-bg-4) 74.51% - ); - - opacity: var(--bar-opacity); - background-blend-mode: var(--bar-blend-mode); - background: var(--bar-gradient); - - position: absolute; - pointer-events: none; - -webkit-user-select: none; - user-select: none; - z-index: 0; - width: 100%; - bottom: 0; - height: 100%; - filter: blur(100px); - transform: translateZ(0); - } - - @include media($max: $mobile) { - .radialContainer { - .radialGradient { - right: -180%; - width: 300%; - bottom: -55%; - } - } - } -} \ No newline at end of file diff --git a/styles/sections/index/looking.module.scss b/styles/sections/index/looking.module.scss deleted file mode 100644 index d1e43671c..000000000 --- a/styles/sections/index/looking.module.scss +++ /dev/null @@ -1,84 +0,0 @@ -// Section -.section { - background-color: var(--background-dim); - - h2 { - font-size: var(--font-xl-l); - } - - h4 { - font-family: var(--font-accent); - text-transform: none; - font-size: var(--font-r-s); - letter-spacing: .01rem; - } - - .container { - text-align: left; - max-width: 700px; - } - - .copy { - width: fit-content; - display: flex; - flex-direction: column; - } - - .jsonSub { - line-height: .9; - margin: 0.25rem 0; - margin-left: 2rem; - } - - .preHeader { - font-family: var(--font-subheader); - font-weight: 400; - letter-spacing: -.05rem; - font-size: 2rem; - } - .header { - color: var(--secondary); - } - .subHeader { - font-family: var(--font-subheader); - color: var(--primary-bright); - font-weight: 400; - letter-spacing: -.05rem; - text-align: right; - font-size: 2rem; - } - - .highlight { - --looking-bg-1: var(--neon-1-1); - --looking-bg-2: var(--neon-1-2); - - background: var(--bar-gradient); - --bar-gradient: - linear-gradient( - 90deg, - var(--looking-bg-1) -45%, - var(--looking-bg-2) 125% - ); - - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - } - - .highlight2 { - --looking-bg-1: var(--neon-2-1); - --looking-bg-2: var(--neon-2-2); - - background: var(--bar-gradient); - --bar-gradient: - linear-gradient( - 90deg, - var(--looking-bg-1) -45%, - var(--looking-bg-2) 125% - ); - - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - } -} \ No newline at end of file