From c63a2adf4e869e4ec996b47e01a8e56af7733fd6 Mon Sep 17 00:00:00 2001 From: SSHari Date: Sat, 15 Nov 2025 16:40:35 -0600 Subject: [PATCH] feat: update overall theme --- app/components/AnimatedName.tsx | 6 +- app/components/Header.tsx | 84 +++++++------ app/routes/_index.tsx | 156 +++++++++++++++--------- app/routes/about.tsx | 203 +++++++++++++++++++++++--------- app/tailwind.css | 76 ++++++++++-- tailwind.config.js | 20 ++++ 6 files changed, 372 insertions(+), 173 deletions(-) diff --git a/app/components/AnimatedName.tsx b/app/components/AnimatedName.tsx index e2f4344..fc6513e 100644 --- a/app/components/AnimatedName.tsx +++ b/app/components/AnimatedName.tsx @@ -31,7 +31,7 @@ export default function AnimatedName(props: AnimatedNameProps) { Sai Hari {namePaths.map(({ letter, path }, index) => ( - - cls( - 'rtl inline-block text-lg font-medium transition-colors duration-200 after:block after:h-[2px] after:w-0 after:bg-gray-950 after:transition-[width] after:duration-200 hover:text-gray-950 hover:after:w-full', - { 'text-gray-500': !isActive, 'text-gray-950': isActive }, - ) - } - /> - - ); -} - export function Header() { const match = useMatches(); const { id } = match[match.length - 1]; @@ -44,24 +12,52 @@ export function Header() { return (
- -
+ + + cls('group relative text-base font-medium transition-colors', { + 'text-warm-gray-400 hover:text-warm-black': !isActive, + 'text-warm-black': isActive, + }) + } + > + {({ isActive }) => ( + <> + About + + + )} + + + +
); } diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 2a2cb66..a419298 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,7 +1,7 @@ -import { motion } from 'framer-motion'; +import { AnimatePresence, motion } from 'framer-motion'; +import { useEffect, useState } from 'react'; import { ClientOnly } from 'remix-utils/client-only'; import AnimatedName from '~/components/AnimatedName'; -import { IconLink } from '~/components/IconLink'; import { BlogIcon } from '~/icons/BlogIcon'; import { GitHubIcon } from '~/icons/GitHubIcon'; import { LinkedInIcon } from '~/icons/LinkedInIcon'; @@ -13,58 +13,104 @@ export const meta = () => { export default function Index() { return ( -
-
-

- -

- +
+ {/* Animated Name - Hero */} + - - {() => <>{[...tagLines].sort(() => 0.5 - Math.random())[0]}} - - -
- {iconConfigs.map((config, index) => ( - + + + + + {/* Tagline with cycling animation */} + {() => } + + {/* Social Links */} + + {socialLinks.map((link, index) => ( + - - +
+ + +
+ + {link.label} + +
))} -
+ + + {/* Footer note */} + + Developer. Builder. Thinker. +
); } +function CyclingTagline({ tagLines }: { tagLines: string[] }) { + const [currentIndex, setCurrentIndex] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + setCurrentIndex((prev) => (prev + 1) % tagLines.length); + }, 4000); + return () => clearInterval(interval); + }, [tagLines.length]); + + return ( + + + + {tagLines[currentIndex]} + + + + ); +} + const tagLines = [ 'Developer of Software', 'I think, therefore I program.', @@ -72,25 +118,25 @@ const tagLines = [ 'Fastest Code-Slinger in the West', ]; -const iconConfigs = [ +const socialLinks = [ { - title: 'twitter', + label: 'Twitter', + href: 'https://twitter.com/TheSSHGuy', icon: TwitterIcon, - link: 'https://twitter.com/TheSSHGuy', }, { - title: 'linkedin', - icon: LinkedInIcon, - link: 'https://www.linkedin.com/in/sshari/', + label: 'GitHub', + href: 'https://github.com/SSHari', + icon: GitHubIcon, }, { - title: 'github', - icon: GitHubIcon, - link: 'https://github.com/SSHari', + label: 'LinkedIn', + href: 'https://www.linkedin.com/in/sshari/', + icon: LinkedInIcon, }, { - title: 'blog', + label: 'Blog', + href: 'https://blog.thesshguy.com', icon: BlogIcon, - link: 'https://blog.thesshguy.com', }, ]; diff --git a/app/routes/about.tsx b/app/routes/about.tsx index 2237278..bf4b44f 100644 --- a/app/routes/about.tsx +++ b/app/routes/about.tsx @@ -1,6 +1,5 @@ +import { Link } from '@remix-run/react'; import { motion } from 'framer-motion'; -import { ExternalLink } from '~/components/ExternalLink'; -import { ArrowCircleRightIcon } from '~/icons/ArrowCircleRight'; export const meta = () => { return [{ title: 'Sai Hari | About' }]; @@ -8,66 +7,154 @@ export const meta = () => { export default function About() { return ( -
- -

- About Me -

+
+
+ {/* Back link */} + + + + ← + + Back + + -

- Hey there! My name is Sai and I like to build things. Usually I'm - building for the web, but as long as I'm having fun, the{' '} - where isn't so important. -

+ {/* About content */} + + {/* Header */} +
+

+ About Me +

+
+
-

- Today that building happens at{' '} - - Dragos - - , where our mission is to{' '} - safeguard civilization. My team is - pretty small, which is nice, because it means I get to build things - that span across our tech stack. -

+ {/* Content sections */} +
+ + Hey there! My name is Sai and I like to build things. Usually I'm + building for the web, but as long as I'm having fun, the{' '} + where isn't so + important. + -

- Here are some of the more recent technologies I've been working with: -

+ + Today that building happens at{' '} + + Dragos + + , where our mission is to{' '} + + safeguard civilization + + . My team is pretty small, which is nice, because it means I get + to build things that span across our tech stack. + +
-
    - {[ - 'React', - 'Node', - 'TypeScript', - 'Elasticsearch', - 'AWS Services', - 'Terraform', - 'Docker', - 'Kubernetes', - 'Lua', - 'Neovim', - ].map((tech) => ( -
  • - - {tech} -
  • - ))} -
- + {/* Tech stack */} + +

+ Recent Technologies +

+
+ {technologies.map((tech, index) => ( + + + {String(index + 1).padStart(2, '0')} + + + {tech} + + + ))} +
+
+ + {/* Optional: Get in touch section */} + +

+ Let's connect +

+

+ I'm always interested in hearing about new projects and + opportunities. Feel free to reach out on{' '} + + Twitter + {' '} + or{' '} + + LinkedIn + + . +

+
+ +
); } + +const technologies = [ + 'React', + 'Node', + 'TypeScript', + 'Elasticsearch', + 'AWS Services', + 'Terraform', + 'Docker', + 'Kubernetes', + 'Lua', + 'Neovim', +]; diff --git a/app/tailwind.css b/app/tailwind.css index 68a257c..5db8764 100644 --- a/app/tailwind.css +++ b/app/tailwind.css @@ -26,20 +26,39 @@ :root { /* Theme CSS Variables */ - /* COLORS */ - --color-white: 0 0% 100%; + /* COLORS - Warm Minimalist Palette */ + --color-cream: 40 25% 98%; + --color-warm-white: 35 20% 97%; + --color-off-white: 30 15% 96%; + + --color-warm-black: 0 0% 10%; + --color-charcoal: 0 0% 15%; + --color-slate: 0 0% 25%; + + --color-terracotta: 14 70% 58%; + --color-terracotta-dark: 14 65% 48%; + --color-rust: 18 60% 45%; + + --color-warm-gray-100: 30 8% 92%; + --color-warm-gray-200: 30 6% 85%; + --color-warm-gray-300: 30 5% 70%; + --color-warm-gray-400: 30 4% 55%; + --color-warm-gray-500: 30 3% 40%; + --color-warm-gray-600: 30 3% 30%; - --color-gray-50: 210, 20%, 98%; - --color-gray-100: 220 14% 96%; - --color-gray-200: 220 13% 91%; - --color-gray-300: 216 12% 84%; - --color-gray-400: 218 11% 65%; - --color-gray-500: 220 9% 46%; - --color-gray-600: 215 14% 34%; - --color-gray-700: 217 19% 27%; - --color-gray-800: 215 28% 17%; - --color-gray-900: 221 39% 11%; - --color-gray-950: 228 84% 4%; + /* Legacy gray variables for compatibility */ + --color-white: 0 0% 100%; + --color-gray-50: 30 15% 98%; + --color-gray-100: 30 12% 95%; + --color-gray-200: 30 8% 90%; + --color-gray-300: 30 6% 80%; + --color-gray-400: 30 5% 60%; + --color-gray-500: 30 4% 45%; + --color-gray-600: 30 3% 30%; + --color-gray-700: 0 0% 20%; + --color-gray-800: 0 0% 15%; + --color-gray-900: 0 0% 10%; + --color-gray-950: 0 0% 5%; } /* Global Styles */ @@ -65,6 +84,37 @@ body, margin: 0; } +body { + background-color: hsl(var(--color-cream)); + color: hsl(var(--color-warm-black)); + position: relative; +} + +/* Subtle grain texture overlay */ +body::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.03; + z-index: 9999; + pointer-events: none; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); +} + .rtl { direction: rtl; } + +/* Smooth scroll behavior */ +html { + scroll-behavior: smooth; +} + +/* Selection color */ +::selection { + background-color: hsl(var(--color-terracotta) / 0.2); + color: hsl(var(--color-warm-black)); +} diff --git a/tailwind.config.js b/tailwind.config.js index ce5c7aa..e12f657 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -8,6 +8,25 @@ module.exports = { transparent: 'transparent', current: 'currentColor', white: 'hsl(var(--color-white))', + cream: 'hsl(var(--color-cream))', + 'warm-white': 'hsl(var(--color-warm-white))', + 'off-white': 'hsl(var(--color-off-white))', + 'warm-black': 'hsl(var(--color-warm-black))', + charcoal: 'hsl(var(--color-charcoal))', + slate: 'hsl(var(--color-slate))', + terracotta: { + DEFAULT: 'hsl(var(--color-terracotta))', + dark: 'hsl(var(--color-terracotta-dark))', + }, + rust: 'hsl(var(--color-rust))', + 'warm-gray': { + 100: 'hsl(var(--color-warm-gray-100))', + 200: 'hsl(var(--color-warm-gray-200))', + 300: 'hsl(var(--color-warm-gray-300))', + 400: 'hsl(var(--color-warm-gray-400))', + 500: 'hsl(var(--color-warm-gray-500))', + 600: 'hsl(var(--color-warm-gray-600))', + }, gray: { 50: 'hsl(var(--color-gray-50))', 100: 'hsl(var(--color-gray-100))', @@ -25,6 +44,7 @@ module.exports = { extend: { fontFamily: { sans: ['Satoshi-Variable', ...defaultTheme.fontFamily.sans], + mono: ['JetBrains Mono', ...defaultTheme.fontFamily.mono], }, }, },