7.2
Komponen Link
Katalog tautan di src/components/links/ dibangun berlapis. UnstyledLink adalah satu-satunya komponen yang memutuskan bagaimana sebuah tautan dirender; lima komponen lain (ArrowLink, ButtonLink, IconLink, PrimaryLink, UnderlineLink) membungkusnya dan menambahkan kelas Tailwind. Halaman ini membahas rantai itu beserta props tambahannya. Tombol biasa seperti Button, IconButton, dan TextButton dibahas di Komponen Tombol.
Memilih next/link atau anchor tab baru
UnstyledLinkProps menambahkan lima hal di atas props anchor bawaan React: href, children, openNewTab, className, dan nextLinkProps yang bertipe Omit<LinkProps, 'href'> src/components/links/UnstyledLink.tsx:6-121. Komponennya adalah React.forwardRef ke HTMLAnchorElement, jadi ref dari pemanggil tetap sampai ke elemen <a> src/components/links/UnstyledLink.tsx:14-192.
Keputusan render berasal dari satu ekspresi. Bila openNewTab diberikan secara eksplisit, nilainya menang. Bila tidak, tautan dianggap eksternal ketika href tidak diawali / dan tidak diawali # src/components/links/UnstyledLink.tsx:14-192. Konsekuensinya, mailto: dan tel: juga dirender sebagai anchor tab baru, sedangkan #section tetap memakai next/link.
graph TD
A["UnstyledLink: hitung isNewTab"] --> B{"openNewTab terdefinisi?"}
B -- "ya" --> C["isNewTab = openNewTab"]
B -- "tidak" --> D{"href diawali '/' atau '#'?"}
D -- "ya" --> E["isNewTab = false"]
D -- "tidak" --> F["isNewTab = true"]
C --> G{"isNewTab?"}
E --> G
F --> G
G -- "false" --> H["next/link Link, nextLinkProps ikut di-spread"]
G -- "true" --> I["a target='_blank' rel='noopener noreferrer', kelas cursor-newtab"]
style A fill:#f59e0b,color:#000
style H fill:#2d7d46,color:#fff
style I fill:#8b5cf6,color:#fff
%% fig: Cabang render UnstyledLink
%% desc: UnstyledLink memilih next/link untuk tautan internal dan anchor tab baru untuk tautan eksternal, berdasarkan nilai openNewTab atau bentuk href.Cabang pertama mengembalikan next/link dan menyebarkan ...nextLinkProps setelah ...rest, sehingga properti di dalam nextLinkProps menimpa props anchor yang bernama sama src/components/links/UnstyledLink.tsx:21-333. Cabang kedua mengembalikan <a> dengan target='_blank' dan rel='noopener noreferrer', lalu menambahkan kelas cursor-newtab lewat cn('cursor-newtab', className) src/components/links/UnstyledLink.tsx:35-464. Kelas itu didefinisikan di src/styles/globals.css sebagai kursor gambar /images/new-tab.png src/styles/globals.css:77-795.
⚠️
nextLinkPropshanya di-spread di cabangnext/link. Untuk tautan eksternal, seluruh isinextLinkPropsdiabaikan, jadiprefetchataureplacetidak berpengaruh di sana src/components/links/UnstyledLink.tsx:21-466.
Rantai pewarisan props
Semua gaya dibangun dari tipe yang sama. PrimaryLink, ButtonLink, dan IconLink memperluas UnstyledLinkProps dengan props varian, UnderlineLink memakai UnstyledLinkProps apa adanya, dan ArrowLink menambahkan as serta direction di atasnya.
graph TD
UL["UnstyledLink + UnstyledLinkProps"]
UL -- "variant primary / basic" --> PL["PrimaryLink"]
UL -- "tanpa props tambahan" --> ULL["UnderlineLink"]
UL -- "variant x5, size sm / base, leftIcon, rightIcon" --> BL["ButtonLink"]
UL -- "variant x5, icon, tanpa children" --> IL["IconLink"]
ULL -- "komponen default" --> AL["ArrowLink: direction, as"]
BL -- "as={ButtonLink}" --> AL
style UL fill:#f59e0b,color:#000
style AL fill:#8b5cf6,color:#fff
%% fig: Pewarisan props komponen link
%% desc: Seluruh komponen link mewarisi UnstyledLinkProps; ArrowLink dapat dibungkuskan ke UnderlineLink secara default atau ke komponen lain lewat prop as.| Komponen | Props tambahan | Nilai default | Ciri render |
|---|---|---|---|
UnstyledLink | openNewTab, nextLinkProps | tanpa gaya | memilih next/link atau <a target='_blank'> src/components/links/UnstyledLink.tsx:6-121 |
PrimaryLink | variant: primary | basic | primary | teks berwarna, tanpa border src/components/links/PrimaryLink.tsx:9-127 |
UnderlineLink | tidak ada | — | border bawah titik-titik dan animated-underline src/components/links/UnderlineLink.tsx:9-208 |
ButtonLink | variant, size, leftIcon, rightIcon, isDarkBg, classNames | primary, base | tampilan tombol inline-flex rounded-sm src/components/links/ButtonLink.tsx:20-309 |
IconLink | variant, icon, isDarkBg, classNames | outline | kotak ikon tanpa children src/components/links/IconLink.tsx:19-2610 |
ArrowLink | as, direction | UnderlineLink, right | teks plus SVG panah src/components/links/ArrowLink.tsx:8-1211 |
PrimaryLink
Daftar variannya hanya berisi primary dan basic, dan nilai default-nya primary src/components/links/PrimaryLink.tsx:9-127. Kelas dasarnya inline-flex items-center dengan ring fokus focus-visible:ring-primary-500 focus-visible:ring-3 focus-visible:ring-offset-2 dan font-medium; varian primary memakai text-primary-500 hover:text-primary-600 active:text-primary-700, sedangkan varian basic memakai text-black hover:text-gray-600 active:text-gray-800 src/components/links/PrimaryLink.tsx:14-3312.
UnderlineLink
Komponen ini menerima UnstyledLinkProps tanpa menambah props apa pun src/components/links/UnderlineLink.tsx:9-208. Kelasnya animated-underline custom-link inline-flex items-center font-medium ditambah border-dark border-b border-dotted hover:border-black/0. Efek animasinya berasal dari utilitas .animated-underline: dua linear-gradient dengan background-size: 100% 2px, 0 2px yang berubah menjadi 0 2px, 100% 2px saat :hover atau :focus-visible, dan transition: 0.3s ease hanya aktif bila prefers-reduced-motion: no-preference src/styles/globals.css:129-15013. Di dalam wadah .bg-dark, warna border tautan custom-link diganti menjadi border-gray-200 agar tetap terbaca src/styles/globals.css:118-12014.
ButtonLink dan IconLink
ButtonLinkVariant berisi lima nilai (primary, outline, ghost, light, dark) dan ButtonLinkSize berisi sm serta base src/components/links/ButtonLink.tsx:11-1815. Props-nya menambahkan isDarkBg, variant, size, leftIcon, rightIcon, dan objek classNames untuk tiap ikon src/components/links/ButtonLink.tsx:20-309; default-nya variant = 'primary' dan size = 'base' src/components/links/ButtonLink.tsx:32-49. Ukuran base memakai px-3 py-1.5 dengan text-sm md:text-base, sedangkan sm memakai px-2 py-1 dengan text-xs md:text-sm. Ikon dibungkus div dengan mr-1 (base) atau mr-1.5 (sm) untuk leftIcon, ml-1 atau ml-1.5 untuk rightIcon, dan dirender dengan size='1em' src/components/links/ButtonLink.tsx:102-120. isDarkBg hanya mengubah varian outline dan ghost, yang mendapat latar hover:bg-gray-900 active:bg-gray-800 src/components/links/ButtonLink.tsx:71-84.
IconLink memakai daftar varian yang sama tetapi tipe props-nya Omit<UnstyledLinkProps, 'children'>, sehingga komponen ini tidak menerima children src/components/links/IconLink.tsx:19-2610. Default variannya outline, dan UnstyledLink dipanggil dengan type='button' serta ukuran kotak min-h-[28px] min-w-[28px] p-1 md:min-h-[34px] md:min-w-[34px] md:p-2 src/components/links/IconLink.tsx:41-4919. Isinya hanya <Icon size='1em' className={cn(classNames?.icon)} /> bila props icon diberikan src/components/links/IconLink.tsx:88-9120.
ArrowLink
ArrowLinkProps menggabungkan as bertipe React.ElementType, direction bertipe 'left' | 'right', UnstyledLinkProps, dan React.ComponentProps<C> src/components/links/ArrowLink.tsx:8-1211. Komponen yang dirender adalah as || UnderlineLink, dengan direction default right src/components/links/ArrowLink.tsx:14-2121. Wadahnya memakai group gap-[0.25em], dan direction === 'left' menambahkan flex-row-reverse src/components/links/ArrowLink.tsx:26-3122. SVG panahnya memakai direction === 'right' ? 'motion-safe:-translate-x-1' : 'rotate-180' dan bergerak ke group-hover:translate-x-0 saat wadah di-hover src/components/links/ArrowLink.tsx:39-4423.
Karena as bertipe React.ElementType, props komponen tujuan ikut dikenali. Katalog memakai as={UnstyledLink} dan as={ButtonLink} dengan variant='light', sehingga panah bisa dipasang pada tautan polos maupun pada tombol src/app/components/page.tsx:174-19324.
Pemakaian di halaman
Halaman beranda mengimpor ArrowLink, ButtonLink, UnderlineLink, dan UnstyledLink src/app/page.tsx:6-925. ArrowLink dipakai untuk menuju repository GitHub dengan href absolut, ButtonLink dengan variant='light' menuju /components yang internal, dan UnstyledLink membungkus gambar badge Vercel src/app/page.tsx:36-4326, src/app/page.tsx:45-5627.
Halaman pratinjau mengimpor lima komponen, yaitu ArrowLink, ButtonLink, PrimaryLink, UnderlineLink, dan UnstyledLink, tanpa IconLink src/app/components/page.tsx:17-2128. Tiap bagian katalog menampilkan pasangan tautan internal / dan tautan eksternal https://theodorusclarence.com, misalnya pada bagian UnstyledLink src/app/components/page.tsx:130-14029. Bagian ButtonLink menulis keterangan "Button styled link with 3 variants" padahal ButtonLinkVariant berisi lima nilai, dan kelima varian itu memang dirender semua src/app/components/page.tsx:196-200, src/app/components/page.tsx:202-230, src/components/links/ButtonLink.tsx:11-18.
Untuk menambah varian baru atau mengubah warnanya, kelas yang dipakai komponen ini bergantung pada token primary-* dan dark yang dijelaskan di Styling, Token Warna, dan Tema.