Last active
February 10, 2026 10:43
-
-
Save tomhermans/ae12d6c5437fcc53b2d73073b0ccf082 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // src/lib/plugins/externalLink.ts | |
| /** | |
| * usage: | |
| * | |
| export default defineConfig({ | |
| //... more config | |
| markdown: { | |
| rehypePlugins: [ | |
| [ | |
| externalLink, | |
| { | |
| domains: [ | |
| // domains you need this on | |
| "localhost:3144", | |
| "staging.mysite.app", | |
| "mysite.com", | |
| ], | |
| }, | |
| ], | |
| ], | |
| }, | |
| //... more config | |
| }); | |
| */ | |
| /** | |
| * if you do not have a sr-only class in your site, | |
| add the inline style to to the span, | |
| uncomment line 47: | |
| style: "position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;", | |
| */ | |
| // modified from https://dsalvagni.com/b/astro-plugin-open-external-links-in-new-tab/ | |
| // and https://projectbrackets.com/labz/astro-markdown-target-blank-solution/ | |
| import { visit } from "unist-util-visit"; | |
| import type { Element } from "hast"; | |
| export const externalLink = (options = {}) => { | |
| const internalDomains = options.domains || []; | |
| return (tree) => { | |
| visit(tree, "element", (node: Element) => { | |
| if (node.tagName !== "a" || !node.properties?.href) return; | |
| const href = node.properties.href.toString(); | |
| if (!href.startsWith("http")) return; | |
| try { | |
| const url = new URL(href); | |
| const isInternal = internalDomains.some((domain) => { | |
| const cleanDomain = domain.split(":")[0]; | |
| return ( | |
| url.hostname === cleanDomain || | |
| url.hostname.endsWith(`.${cleanDomain}`) | |
| ); | |
| }); | |
| if (!isInternal) { | |
| node.properties.target = "_blank"; | |
| node.properties.rel = "noopener noreferrer"; | |
| // Inject the accessible span using your existing CSS class | |
| node.children.push({ | |
| type: "element", | |
| tagName: "span", | |
| properties: { | |
| className: ["sr-only"], | |
| /* | |
| style: "position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;", | |
| */ | |
| }, | |
| children: [{ type: "text", value: " (opens in a new tab)" }], | |
| }); | |
| } | |
| } catch (e) { | |
| // Not a valid URL | |
| } | |
| }); | |
| }; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment