65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
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;
|