Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
bad4b33
feat(certificates): add Front-End Development library certificate fro…
CodingWithJiro Sep 6, 2026
600e8fb
chore(meta): update metadata for article slug page to show blog title…
CodingWithJiro Sep 6, 2026
58e0d11
test(experiences): update data to use the CERTIFICATES data instead o…
CodingWithJiro Sep 7, 2026
caf5f07
feat(tech-stack): add View all link in the Tech Stack section of the …
CodingWithJiro Sep 7, 2026
8bff087
feat(tech-stack): create the initial tech stack page
CodingWithJiro Sep 7, 2026
5c1c81e
feat(ux): add You are here! label when user hovers over the current p…
CodingWithJiro Sep 8, 2026
a59fe21
feat(tech-stack): create initial tech stack data
CodingWithJiro Sep 9, 2026
2f62b82
feat(tech-stack): complete list of tech used so far and export landin…
CodingWithJiro Sep 9, 2026
9d0b17d
refactor(tech-stack): update data in tech stack section and test
CodingWithJiro Sep 9, 2026
76694cc
feat(tech-stack): add more technologies and create a TECH_STACK objec…
CodingWithJiro Sep 9, 2026
846ff65
feat(tech-list): create a TechList component section
CodingWithJiro Sep 9, 2026
87d67e2
refactor(tech-stack): use TechList component
CodingWithJiro Sep 9, 2026
5cf656b
feat(tech-stack): add lists and content to tech stack page
CodingWithJiro Sep 9, 2026
7d3517d
feat(ux): add color transition and hover border effect to each tech l…
CodingWithJiro Sep 9, 2026
7da8e16
refactor(view-all-link): remove color transition
CodingWithJiro Sep 9, 2026
9e34f56
test(tech-stack): renders view all link
CodingWithJiro Sep 10, 2026
5a1dd06
test(e2e): visitor can see the Tech Stack section
CodingWithJiro Sep 10, 2026
ecaab47
test(e2e): visitor can view the Tech Stack page
CodingWithJiro Sep 10, 2026
97ab1db
refactor(test): verify page URL to for tech stack page
CodingWithJiro Sep 10, 2026
7557080
feat(ux): add color transition to view all links when tabbed into
CodingWithJiro Sep 10, 2026
3007fc6
feat(ux): polish outline offset, color transitions, hover, and focus …
CodingWithJiro Sep 10, 2026
80fc504
test(e2e): visitor can tab to the Tech Stack view all link and open t…
CodingWithJiro Sep 10, 2026
3eceb8a
test(e2e): visitor can tab to the Projects view all link and open the…
CodingWithJiro Sep 10, 2026
df6ef39
test(e2e): visitor can tab to the Blog view all link and open the Blo…
CodingWithJiro Sep 10, 2026
a53cbd5
feat(404): create a not-found page
CodingWithJiro Sep 10, 2026
2bfe012
feat(ux): add hover border effect on profile picture
CodingWithJiro Sep 10, 2026
44d0ed6
feat(ux): update hover color in each project item in home page
CodingWithJiro Sep 10, 2026
1794cad
refactor(projects): use Link from next for Projects section links
CodingWithJiro Sep 10, 2026
885ae20
refactor(footer): replace anchor tag with Link from next
CodingWithJiro Sep 10, 2026
30eb73b
feat(ux): update border hover color for banner images in blog section…
CodingWithJiro Sep 10, 2026
9c7293d
feat(ux): remove scale hover and update border color hover for banner…
CodingWithJiro Sep 10, 2026
4c82fab
test(e2e): visitor sees a 404 Page not found when going to an unknown…
CodingWithJiro Sep 11, 2026
1361265
feat(ux): change hover border color to each project in Projects page
CodingWithJiro Sep 11, 2026
eab1998
feat(ux): add hover delay to the tooltip in current page breadcrumb
CodingWithJiro Sep 12, 2026
69deb10
perf(skip-link): use Link instead of anchor tag
CodingWithJiro Sep 12, 2026
ea5aaf3
perf(experience): use Link instead of anchor tag for certification links
CodingWithJiro Sep 12, 2026
f781c44
perf(hero): use Link instead of anchor tag for social links
CodingWithJiro Sep 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions src/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,44 @@ import ArticleHeader from '@/components/sections/article-header';
import Footer from '@/components/sections/footer';
import Link from 'next/link';
import { ArrowLeft, ArrowRight } from 'lucide-react';
import type { Metadata } from 'next';

const metaDescription =
'Blog posts on full stack development and tech from Elmar Chavez.';

type ArticlePageProps = {
params: Promise<{ slug: string }>;
};

export async function generateMetadata({
params,
}: ArticlePageProps): Promise<Metadata> {
const { slug } = await params;
const blog = getBlogMeta(slug);
const title = `${blog.title} | Elmar Chavez`;
const metadata: Metadata = {
title,
description: metaDescription,
keywords: [
'Elmar Chavez Blog',
'Web Development Blog',
'Tech Articles',
'Tech Community',
'Software Engineering Blog',
],
openGraph: {
title,
description: metaDescription,
},
twitter: {
card: 'summary_large_image',
title,
description: metaDescription,
},
};
return metadata;
}

export default async function ArticlePage({ params }: ArticlePageProps) {
const { slug } = await params;
const blog = getBlogMeta(slug);
Expand Down Expand Up @@ -48,15 +81,15 @@ export default async function ArticlePage({ params }: ArticlePageProps) {
<div className="flex items-center justify-between">
<Link
href="/"
className="focus-visible:outline-muted-foreground flex items-center gap-1 rounded-sm text-[0.85rem] font-normal underline-offset-4 outline-2 outline-offset-4 outline-transparent hover:underline md:text-[0.875rem]"
className="focus-visible:outline-muted-foreground flex items-center gap-1 rounded-sm text-[0.85rem] font-normal underline-offset-4 outline-2 outline-offset-6 outline-transparent hover:underline focus-visible:transition-colors focus-visible:duration-150 focus-visible:ease-in-out md:text-[0.875rem]"
>
<ArrowLeft aria-hidden="true" size={16} />
<span>Back to homepage</span>
</Link>

<Link
href="/blog"
className="focus-visible:outline-muted-foreground flex items-center gap-1 rounded-sm text-right text-[0.85rem] font-normal underline-offset-4 outline-2 outline-offset-4 outline-transparent hover:underline md:text-[0.875rem]"
className="focus-visible:outline-muted-foreground flex items-center gap-1 rounded-sm text-right text-[0.85rem] font-normal underline-offset-4 outline-2 outline-offset-6 outline-transparent hover:underline focus-visible:transition-colors focus-visible:duration-150 focus-visible:ease-in-out md:text-[0.875rem]"
>
<span>Read more articles</span>
<ArrowRight aria-hidden="true" size={16} />
Expand Down
4 changes: 2 additions & 2 deletions src/app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ export default function BlogPage() {
className="border-border/60 border-b pb-4 transition-colors duration-150 ease-in-out last:border-b-0 md:flex md:flex-row md:items-center md:justify-between md:gap-4"
key={id}
>
<div className="border-ring/20 focus-within:border-ring has-hover:border-ring hidden w-55 shrink-0 overflow-hidden rounded-xl border-2 transition-all duration-150 ease-in-out md:block">
<div className="border-ring/20 focus-within:border-foreground has-hover:border-foreground hidden w-55 shrink-0 overflow-hidden rounded-xl border transition-all duration-150 ease-in-out md:block">
<Image
src={imgUrl}
alt={imgAlt}
width={800}
height={400}
loading="eager"
className="aspect-video w-full object-cover transition-transform duration-150 hover:scale-105"
className="aspect-video w-full object-cover"
/>
</div>

Expand Down
29 changes: 29 additions & 0 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Footer from '@/components/sections/footer';
import Breadcrumb from '@/components/sections/breadcrumb';
import ThemeToggle from '@/components/theme/theme-toggle';

export default function NotFound() {
return (
<main
id="main"
className="mx-auto flex min-h-screen max-w-130 flex-col justify-between px-4 py-8 md:max-w-4xl"
>
<header>
<div className="mb-12 flex items-center justify-between">
<Breadcrumb items={[]} currentLabel="Page not found" />
<ThemeToggle />
</div>

<h1 className="mb-4 text-[36px] leading-10 font-extrabold">
Page not found
</h1>
<p className="text-muted-foreground text-base">
Seems like this page doesn&apos;t exist{' '}
<span className="whitespace-nowrap">¯\_(ツ)_/¯</span>
</p>
</header>

<Footer />
</main>
);
}
22 changes: 13 additions & 9 deletions src/app/projects/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ export default function ProjectsPage() {
</header>

<section>
<h1 className="mb-1 text-lg font-bold md:text-3xl">Projects</h1>
<p className="mb-6 text-sm font-medium md:text-base">
<h1 className="mb-1 text-lg font-bold transition-colors duration-150 ease-in-out md:text-3xl">
Projects
</h1>
<p className="mb-6 text-sm font-medium transition-colors duration-150 ease-in-out md:text-base">
Things I built and worked on.
</p>
</section>
Expand All @@ -64,7 +66,7 @@ export default function ProjectsPage() {
}) => {
return (
<li
className="border-ring/20 focus-within:border-ring has-hover:border-ring bg-background text-foreground mx-auto flex max-w-60 flex-col overflow-hidden rounded-lg border transition-all duration-150 ease-in-out min-[850px]:max-w-70"
className="border-ring/40 focus-within:border-foreground has-hover:border-foreground bg-background text-foreground mx-auto flex max-w-60 flex-col overflow-hidden rounded-lg border duration-150 ease-in-out hover:transition-colors focus-visible:transition-colors min-[850px]:max-w-70"
key={id}
>
<div className="relative mb-4 aspect-video max-w-60 shrink-0 overflow-hidden min-[850px]:max-w-70">
Expand Down Expand Up @@ -101,17 +103,19 @@ export default function ProjectsPage() {

<section className="flex h-full flex-col justify-between">
<header className="mb-4 px-4">
<h2 className="mb-2 text-[0.875rem] font-semibold">
<h2 className="text-foreground mb-2 text-[0.875rem] font-semibold">
{title}
</h2>
<p className="text-[0.75rem]">{description}</p>
<p className="text-foreground text-[0.75rem]">
{description}
</p>
</header>

<div className="mb-4 flex items-center justify-center gap-2 px-4 text-[0.75rem]">
<Link
href={githubUrl}
target="_blank"
className="bg-background border-border/50 hover:bg-foreground hover:text-background focus-visible:outline-muted-foreground flex items-center justify-center gap-1 rounded-sm border px-4 py-1 outline-offset-4 outline-transparent transition-colors duration-150 ease-in-out focus-visible:outline-2 min-[850px]:px-6"
className="bg-background border-border/50 text-foreground hover:bg-foreground hover:text-background focus-visible:outline-muted-foreground flex items-center justify-center gap-1 rounded-sm border px-4 py-1 outline-offset-4 outline-transparent duration-150 ease-in-out hover:transition-colors focus-visible:outline-2 focus-visible:transition-colors min-[850px]:px-6"
aria-label={`Go to ${title}'s GitHub repository.`}
>
<FaGithub size={12} />
Expand All @@ -120,7 +124,7 @@ export default function ProjectsPage() {
<Link
href={siteUrl}
target="_blank"
className="bg-background border-border/50 text-foreground hover:bg-foreground hover:text-background focus-visible:outline-muted-foreground flex items-center justify-center gap-1 rounded-sm border px-4 py-1 outline-offset-4 outline-transparent transition-colors duration-150 ease-in-out focus-visible:outline-2 min-[850px]:px-6"
className="bg-background border-border/50 text-foreground hover:bg-foreground hover:text-background focus-visible:outline-muted-foreground flex items-center justify-center gap-1 rounded-sm border px-4 py-1 outline-offset-4 outline-transparent duration-150 ease-in-out hover:transition-colors focus-visible:outline-2 focus-visible:transition-colors min-[850px]:px-6"
aria-label={`Go to ${title}'s live website.`}
>
<Globe size={12} />
Expand All @@ -137,15 +141,15 @@ export default function ProjectsPage() {
<div className="flex items-center justify-between">
<Link
href="/"
className="focus-visible:outline-muted-foreground flex items-center gap-1 rounded-sm text-[0.85rem] font-normal underline-offset-4 outline-2 outline-offset-4 outline-transparent hover:underline md:text-[0.875rem]"
className="focus-visible:outline-muted-foreground flex items-center gap-1 rounded-sm text-[0.85rem] font-normal underline-offset-4 outline-2 outline-offset-6 outline-transparent hover:underline focus-visible:transition-colors focus-visible:duration-150 focus-visible:ease-in-out md:text-[0.875rem]"
>
<ArrowLeft aria-hidden="true" size={16} />
<span>Back to homepage</span>
</Link>

<Link
href="https://github.com/CodingWithJiro/Projects-Hub"
className="focus-visible:outline-muted-foreground flex items-center gap-1 rounded-sm text-[0.85rem] font-normal underline-offset-4 outline-2 outline-offset-4 outline-transparent hover:underline md:text-[0.875rem]"
className="focus-visible:outline-muted-foreground flex items-center gap-1 rounded-sm text-[0.85rem] font-normal underline-offset-4 outline-2 outline-offset-6 outline-transparent hover:underline focus-visible:transition-colors focus-visible:duration-150 focus-visible:ease-in-out md:text-[0.875rem]"
target="_blank"
>
<span>View all projects</span>
Expand Down
63 changes: 63 additions & 0 deletions src/app/tech-stack/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { Metadata } from 'next';
import Breadcrumb from '@/components/sections/breadcrumb';
import ThemeToggle from '@/components/theme/theme-toggle';
import Footer from '@/components/sections/footer';
import TechList from '@/components/sections/tech-list';
import { TECH_STACK } from '@/data/tech-stack';

const metaDescription =
'Technologies and tools Elmar Chavez has worked with across his projects.';

export const metadata: Metadata = {
title: 'Elmar Chavez | Tech Stack',
description: metaDescription,
keywords: [
'Elmar Chavez Tech Stack',
'Web Development Technologies',
'Full Stack Development Skills',
'Frontend Tech',
'Backend Tech',
],
openGraph: {
title: 'Tech Stack | Elmar Chavez',
description: metaDescription,
},
twitter: {
card: 'summary_large_image',
title: 'Tech Stack | Elmar Chavez',
description: metaDescription,
},
};

export default function TechStackPage() {
return (
<main
id="main"
className="mx-auto min-h-screen max-w-130 px-4 py-8 md:max-w-4xl"
>
<header className="mb-8 flex items-center justify-between">
<Breadcrumb items={[]} currentLabel="Tech Stack" />
<ThemeToggle />
</header>

<section className="mb-8">
<h1 className="mb-1 text-lg font-bold md:text-3xl">Tech Stack</h1>
<h2 className="mb-6 text-sm font-medium md:text-base">
Technologies and tools I&apos;ve worked with so far.
</h2>
</section>

<ul className="flex flex-col gap-10">
{TECH_STACK.map(({ category, technologies }) => {
return (
<li key={category}>
<TechList category={category} technologies={technologies} />
</li>
);
})}
</ul>

<Footer />
</main>
);
}
6 changes: 4 additions & 2 deletions src/components/accessibility/skip-link.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import Link from 'next/link';

const SkipLink = () => {
return (
<a
<Link
className="focus-visible:outline-muted-foreground focus-visible:text-foreground focus-visible:bg-card focus-visible:border-border/40 sr-only rounded-sm text-sm outline-offset-4 focus-visible:not-sr-only focus-visible:absolute focus-visible:top-4 focus-visible:left-4 focus-visible:border focus-visible:p-2 focus-visible:outline-2"
href="#main"
>
Skip to main content
</a>
</Link>
);
};

Expand Down
6 changes: 3 additions & 3 deletions src/components/sections/article-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ const ArticleHeader = ({ breadcrumbItems, blogMeta }: ArticleHeaderProps) => {
/>
</div>

<ul className="mb-1 flex gap-2 text-[0.65rem] font-light">
<ul className="text-foreground mb-1 flex gap-2 text-[0.65rem] font-light">
{blogMeta.tags.map((tag) => {
return <li key={tag}>{tag}</li>;
})}
</ul>

<h1 className="mb-4 text-[24px] font-extrabold md:text-[32px] md:leading-10">
<h1 className="text-foreground mb-4 text-[24px] font-extrabold md:text-[32px] md:leading-10">
{blogMeta.title}
</h1>

<section>
<div className="flex items-center gap-2">
<div className="text-foreground flex items-center gap-2">
<div className="flex items-center gap-1">
<div className="size-6 overflow-hidden rounded-full">
<Image
Expand Down
4 changes: 2 additions & 2 deletions src/components/sections/blog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const Blog = () => {
className="border-border/60 border-b pb-4 last:border-b-0 md:flex md:flex-row md:items-center md:justify-between md:gap-4"
key={id}
>
<div className="border-ring/20 focus-within:border-ring has-hover:border-ring hidden w-55 shrink-0 overflow-hidden rounded-xl border-2 transition-all duration-150 ease-in-out md:block">
<div className="border-ring/20 focus-within:border-foreground has-hover:border-foreground hidden w-55 shrink-0 overflow-hidden rounded-xl border transition-all duration-150 ease-in-out md:block">
<Image
src={imgUrl}
alt={imgAlt}
Expand Down Expand Up @@ -61,7 +61,7 @@ const Blog = () => {
</ul>

<Link
className="focus-visible:outline-muted-foreground flex w-fit items-center justify-center gap-1 self-end rounded-sm text-[0.75rem] font-normal underline-offset-4 outline-2 outline-offset-4 outline-transparent transition-colors duration-150 ease-in-out hover:underline min-[425px]:self-auto md:text-right"
className="focus-visible:outline-muted-foreground flex w-fit items-center justify-center gap-1 self-end rounded-sm text-[0.75rem] font-normal underline-offset-4 outline-2 outline-offset-6 outline-transparent hover:underline focus-visible:transition-colors focus-visible:duration-150 focus-visible:ease-in-out min-[425px]:self-auto md:text-right"
href={blogUrl}
aria-label={`Read more about ${title}`}
>
Expand Down
22 changes: 14 additions & 8 deletions src/components/sections/breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Breadcrumb = ({ items, currentLabel }: BreadCrumbProps) => {
<ol className="flex items-center gap-1">
<li className="flex items-center">
<Link
className="text-muted-foreground hover:text-foreground focus-visible:text-foreground text-[12px] font-medium outline-transparent transition-colors focus-visible:underline focus-visible:underline-offset-4 md:text-sm"
className="text-muted-foreground hover:text-foreground focus-visible:text-foreground text-[12px] font-medium outline-transparent focus-visible:underline focus-visible:underline-offset-4 md:text-sm"
href="/"
>
Home
Expand All @@ -30,7 +30,7 @@ const Breadcrumb = ({ items, currentLabel }: BreadCrumbProps) => {
</span>

<Link
className="text-muted-foreground hover:text-foreground focus-visible:text-foreground text-[12px] font-medium outline-transparent transition-colors focus-visible:underline focus-visible:underline-offset-4 md:text-sm"
className="text-muted-foreground hover:text-foreground focus-visible:text-foreground text-[12px] font-medium outline-transparent focus-visible:underline focus-visible:underline-offset-4 md:text-sm"
href={href}
>
{label}
Expand All @@ -47,12 +47,18 @@ const Breadcrumb = ({ items, currentLabel }: BreadCrumbProps) => {
/
</span>

<p
className="text-muted-foreground hover:text-foreground max-w-32 truncate text-[12px] font-medium transition-colors hover:cursor-pointer md:max-w-62 md:text-sm"
aria-current="page"
>
{currentLabel}
</p>
<div className="group relative">
<p
className="text-muted-foreground group-hover:text-foreground max-w-32 truncate text-[12px] font-medium hover:cursor-pointer md:max-w-62 md:text-sm"
aria-current="page"
>
{currentLabel}
</p>

<span className="bg-foreground text-background after:bg-foreground pointer-events-none absolute bottom-full left-[75%] rounded-md rounded-bl-none px-2 py-1 text-xs whitespace-nowrap opacity-0 transition-opacity duration-150 ease-in-out group-hover:opacity-100 group-hover:delay-1000">
You are here!
</span>
</div>
</li>
</ol>
</nav>
Expand Down
5 changes: 3 additions & 2 deletions src/components/sections/experience.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FiExternalLink } from 'react-icons/fi';
import { workExperiences, certificates } from '@/data/experiences';
import Link from 'next/link';

const Experience = () => {
return (
Expand Down Expand Up @@ -36,15 +37,15 @@ const Experience = () => {
className="before:border-border before:bg-background first:before:bg-foreground after:bg-border/50 hover:before:bg-foreground relative pl-4.5 before:absolute before:top-1 before:left-0 before:h-3 before:w-3 before:rounded-full before:border-2 before:transition-colors before:duration-150 before:ease-in-out after:absolute after:top-4 after:-bottom-6 after:left-1.5 after:w-px after:transition-colors after:duration-150 after:ease-in-out last:after:hidden md:pl-6"
key={id}
>
<a
<Link
className="group decoration-muted-foreground relative mb-1 inline-block text-[0.85rem] font-semibold decoration-1 underline-offset-4 outline-0 hover:underline focus-visible:underline"
href={urlLink}
target="_blank"
aria-label={`Open ${title} certificate`}
>
{title}
<FiExternalLink className="group-focus-visible:text-foreground text-muted-foreground group-hover:text-foreground absolute top-px -right-5" />
</a>
</Link>
<p className="flex flex-col text-[0.75rem]">
<span>{institution}</span>
<span className="text-muted-foreground text-[0.70rem]">
Expand Down
Loading
Loading