Skip to content

Commit b6275e4

Browse files
committed
refactor: ♻️ highlighting only author link on blog card
1 parent 4d588dc commit b6275e4

2 files changed

Lines changed: 44 additions & 23 deletions

File tree

src/components/blog/BlogFeatured.tsx

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import dayjs from 'dayjs';
33
import { motion } from 'framer-motion';
44
import Image from 'next/image';
55
import Link from 'next/link';
6+
import { useState } from 'react';
67

78
import authorsData from '@/assets/data/authors.json';
89

@@ -36,6 +37,8 @@ export default function BlogFeatured({
3637
category,
3738
authors,
3839
}: FeaturedProps) {
40+
const [authorHovered, setAuthorHovered] = useState(false);
41+
3942
const resolvedAuthors = (authors ?? []).flatMap((id) => {
4043
const data = authorsData[id as keyof typeof authorsData];
4144
return data ? [{ id, ...data }] : [];
@@ -52,26 +55,32 @@ export default function BlogFeatured({
5255
itemScope
5356
itemType="https://schema.org/BlogPosting"
5457
>
55-
<motion.div
56-
variants={mediaV}
57-
transition={{ duration: 0.25 }}
58-
className="relative aspect-[16/9] max-h-[420px] w-full overflow-hidden bg-slate-100 md:aspect-[5/2] md:max-h-[440px] lg:aspect-[21/9] lg:max-h-[460px]"
59-
>
60-
<Image
61-
src={heroImage}
62-
alt={imageAuthor || title}
63-
fill
64-
sizes="(min-width:1280px) 1100px, 100vw"
65-
className="object-cover"
66-
priority
67-
/>
68-
</motion.div>
58+
<Link href={`/blog/${slug}`} className="block" tabIndex={-1} aria-hidden>
59+
<motion.div
60+
variants={mediaV}
61+
transition={{ duration: 0.25 }}
62+
className="relative aspect-[16/9] max-h-[420px] w-full overflow-hidden bg-slate-100 md:aspect-[5/2] md:max-h-[440px] lg:aspect-[21/9] lg:max-h-[460px]"
63+
>
64+
<Image
65+
src={heroImage}
66+
alt={imageAuthor || title}
67+
fill
68+
sizes="(min-width:1280px) 1100px, 100vw"
69+
className="object-cover"
70+
priority
71+
/>
72+
</motion.div>
73+
</Link>
6974

7075
<div className="p-5 sm:p-6">
7176
<div className="mb-2 flex flex-wrap items-center gap-2 text-xs text-slate-600">
7277
{resolvedAuthors.length > 0 && (
7378
<>
74-
<div className="relative z-10 flex items-center gap-1.5">
79+
<div
80+
className="relative z-10 flex items-center gap-1.5"
81+
onMouseEnter={() => setAuthorHovered(true)}
82+
onMouseLeave={() => setAuthorHovered(false)}
83+
>
7584
<Link
7685
href={`/authors/${resolvedAuthors[0].id}`}
7786
className="flex -space-x-1.5 transition-opacity hover:opacity-80"
@@ -129,7 +138,7 @@ export default function BlogFeatured({
129138
<motion.h2
130139
variants={titleV}
131140
transition={{ duration: 0.2 }}
132-
className="group-hover:text-primary text-2xl leading-tight font-bold text-balance text-slate-900 group-hover:underline sm:text-3xl md:text-[1.9rem]"
141+
className={`text-2xl leading-tight font-bold text-balance text-slate-900 sm:text-3xl md:text-[1.9rem] ${authorHovered ? `` : `group-hover:text-primary group-hover:underline`}`}
133142
>
134143
{title}
135144
</motion.h2>
@@ -141,7 +150,9 @@ export default function BlogFeatured({
141150
)}
142151

143152
<div className="mt-4">
144-
<div className="group-hover:text-primary text-sm font-medium underline-offset-4 hover:underline">
153+
<div
154+
className={`text-sm font-medium underline-offset-4 hover:underline ${authorHovered ? `` : `group-hover:text-primary`}`}
155+
>
145156
<span>Read the full story</span>
146157
<Icon
147158
icon="solar:arrow-right-broken"

src/components/blog/BlogListItem.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import dayjs from 'dayjs';
22
import { motion } from 'framer-motion';
33
import Image from 'next/image';
44
import Link from 'next/link';
5+
import { useState } from 'react';
56

6-
import authorsData from '@/assets/data/authors.json';
77
import { slugifyTag } from '@/lib/utils';
88

9+
import authorsData from '@/assets/data/authors.json';
10+
911
type ListItemProps = {
1012
slug: string;
1113
title: string;
@@ -38,6 +40,8 @@ export default function BlogListItem({
3840
category,
3941
authors,
4042
}: ListItemProps) {
43+
const [authorHovered, setAuthorHovered] = useState(false);
44+
4145
const resolvedAuthors = (authors ?? []).flatMap((id) => {
4246
const data = authorsData[id as keyof typeof authorsData];
4347
return data ? [{ id, ...data }] : [];
@@ -53,9 +57,11 @@ export default function BlogListItem({
5357
className="group relative rounded-2xl border border-slate-200 bg-white p-3 sm:p-4"
5458
>
5559
<div className="grid grid-cols-[auto_1fr] items-start gap-4 sm:gap-5">
56-
<div
57-
aria-hidden
60+
<Link
61+
href={`/blog/${slug}`}
5862
className="relative col-start-1 row-span-2 aspect-[16/10] w-32 shrink-0 overflow-hidden rounded-xl bg-slate-100 sm:w-44 lg:w-52"
63+
tabIndex={-1}
64+
aria-hidden
5965
>
6066
<motion.div
6167
variants={thumbV}
@@ -70,13 +76,17 @@ export default function BlogListItem({
7076
className="object-cover"
7177
/>
7278
</motion.div>
73-
</div>
79+
</Link>
7480

7581
<div className="col-start-2 row-start-1 block">
7682
<div className="mb-1 flex flex-wrap items-center gap-1.5 text-[11px] text-slate-600">
7783
{resolvedAuthors.length > 0 && (
7884
<>
79-
<div className="relative z-10 flex items-center gap-1">
85+
<div
86+
className="relative z-10 flex items-center gap-1"
87+
onMouseEnter={() => setAuthorHovered(true)}
88+
onMouseLeave={() => setAuthorHovered(false)}
89+
>
8090
<Link
8191
href={`/authors/${resolvedAuthors[0].id}`}
8292
className="flex -space-x-1 transition-opacity hover:opacity-80"
@@ -123,7 +133,7 @@ export default function BlogListItem({
123133
<motion.h3
124134
variants={titleV}
125135
transition={{ duration: 0.2 }}
126-
className="group-hover:text-primary line-clamp-2 text-[1.05rem] leading-snug font-semibold text-slate-900 group-hover:underline sm:text-lg"
136+
className={`line-clamp-2 text-[1.05rem] leading-snug font-semibold text-slate-900 sm:text-lg ${authorHovered ? `` : `group-hover:text-primary group-hover:underline`}`}
127137
>
128138
{title}
129139
</motion.h3>

0 commit comments

Comments
 (0)