feat(Link): tertiary variant with reversed hover

This commit is contained in:
Ilia Mashkov
2026-07-14 10:35:57 +03:00
parent 7c6da0ca73
commit fd15374134
3 changed files with 18 additions and 3 deletions
+13
View File
@@ -12,6 +12,7 @@ import { Link } from './Link';
const PRIMARY = 'underline underline-offset-2 hover:opacity-60 transition-opacity';
const SECONDARY = 'no-underline opacity-60 hover:opacity-100 sm:brutal-border-bottom transition-opacity';
const TERTIARY = 'no-underline opacity-100 hover:opacity-60 sm:brutal-border-bottom transition-opacity';
describe('internal link', () => {
it('renders an anchor element', () => {
@@ -92,6 +93,18 @@ describe('variant', () => {
}
});
it('tertiary applies reversed-hover classes', () => {
render(
<Link href="/about" variant="tertiary">
About
</Link>,
);
const link = screen.getByRole('link', { name: 'About' });
for (const cls of TERTIARY.split(' ')) {
expect(link).toHaveClass(cls);
}
});
it('secondary does not apply underline', () => {
render(
<Link href="/about" variant="secondary">
+3 -1
View File
@@ -2,7 +2,7 @@ import NextLink from 'next/link';
import type { ReactNode } from 'react';
import { cn } from '$shared/lib';
export type LinkVariant = 'primary' | 'secondary';
export type LinkVariant = 'primary' | 'secondary' | 'tertiary';
/**
* Props for Link.
@@ -24,6 +24,7 @@ interface Props {
* Visual variant.
* primary — text-decoration underline.
* secondary — border-bottom at sm+, no underline on mobile (for icon+label links).
* tertiary — like secondary but reversed: bright by default, dims on hover.
* @default 'primary'
*/
variant?: LinkVariant;
@@ -37,6 +38,7 @@ interface Props {
const VARIANTS = {
primary: 'underline underline-offset-2 hover:opacity-60 transition-opacity',
secondary: 'no-underline opacity-60 hover:opacity-100 sm:brutal-border-bottom transition-opacity',
tertiary: 'no-underline opacity-100 hover:opacity-60 sm:brutal-border-bottom transition-opacity',
} as const satisfies Record<LinkVariant, string>;
/**
+2 -2
View File
@@ -23,7 +23,7 @@ export async function Footer() {
<div className="w-full flex flex-row justify-between gap-4">
<div className="flex flex-wrap items-center gap-6 sm:gap-4">
{email && (
<Link href={email.url} external variant="secondary" className="flex items-center gap-1.5 text-sm">
<Link href={email.url} external variant="tertiary" className="flex items-center gap-1.5 text-sm">
{email.icon && <InlineSvg svg={email.icon} className="inline-flex w-8 h-8 sm:hidden" />}
<span className="hidden sm:block">{email.label}</span>
</Link>
@@ -33,7 +33,7 @@ export async function Footer() {
key={social.id}
href={social.url}
external
variant="secondary"
variant="tertiary"
className="flex items-center gap-1.5 text-sm"
>
{social.icon && <InlineSvg svg={social.icon} className="inline-flex w-8 h-8 sm:hidden" />}