Files
Anelia-Wolf/src/components/ui/TextLink.tsx
T
2026-09-06 17:21:52 +02:00

65 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { cn } from "@/lib/utils";
import { TransitionLink } from "@/components/motion/TransitionLink";
export interface TextLinkProps {
href: string;
label: string;
/** `target="_blank" rel="noopener noreferrer"` + ↗ instead of →. */
external?: boolean;
/** Anchor/arrow glyph override (default → / ↗ / ↓ for `#…`). */
arrow?: string;
className?: string;
}
/** Caps text link style (Jost 500, 13 px, ls .2em) – shared with ledger links.
* `-my-4/py-4` grows the tap target to 45 px without moving the baseline. */
export const TEXT_LINK =
"group/tl -my-4 inline-flex items-baseline gap-2 py-4 font-sans text-[0.8125rem] font-medium uppercase leading-none tracking-[0.2em] text-accent";
/** Underline grows from the left (`background-size 0 → 100%`, 0.4 s). */
export const GROW_UNDERLINE =
"bg-[linear-gradient(currentColor,currentColor)] bg-[length:0%_1px] bg-left-bottom bg-no-repeat pb-1 transition-[background-size] duration-[400ms] ease-[var(--ease-out-quart)] group-hover/tl:bg-[length:100%_1px] group-focus-visible/tl:bg-[length:100%_1px]";
const ARROW = "transition-transform duration-300 ease-[var(--ease-out-quart)] group-hover/tl:translate-x-2";
function isInternal(href: string) {
return href.startsWith("/");
}
/**
* Caps link with an arrow glyph. Internal routes go through the curtain
* (`TransitionLink`); mailto/tel/http are plain anchors.
*/
export function TextLink({ href, label, external = false, arrow, className }: TextLinkProps) {
const glyph = arrow ?? (external ? "↗" : href.startsWith("#") ? "↓" : "→");
const inner = (
<>
<span className={GROW_UNDERLINE}>{label}</span>
<span aria-hidden="true" className={ARROW}>
{glyph}
</span>
</>
);
const cls = cn(TEXT_LINK, className);
if (isInternal(href) && !external) {
return (
<TransitionLink href={href} label={label} className={cls}>
{inner}
</TransitionLink>
);
}
return (
<a
href={href}
target={external ? "_blank" : undefined}
rel={external ? "noopener noreferrer" : undefined}
className={cls}
>
{inner}
</a>
);
}
export default TextLink;