Skip to content

refactor(links): standardize external links site-wide #36

Closed

Description

@p3t3r67x0

Decision update

We have changed the scope of this issue: the Mastodon feed integration is no longer wanted and must be removed again from the implementation branch / PR #37.

The remaining goal is the site-wide external-link behavior that was implemented together with the Mastodon work.

Goal

Introduce one consistent, reusable rule for external links across the entire website:

  • every external HTTP(S) link opens with target="_blank"
  • every such link also includes rel="noopener noreferrer"
  • internal links stay in the same tab
  • same-site absolute URLs for https://oklabflensburg.de/... are treated as internal
  • Nuxt Content / Markdown links follow the same rule centrally
  • localized internal routing, query strings and fragments remain intact

Canonical implementation direction

Keep one shared external-link detector, for example shared/utils/links.ts, based on the canonical site.url origin.

Use a reusable component such as app/components/SiteLink.vue for ordinary Vue links where appropriate, and make app/components/content/ProseA.vue use the same rule for Nuxt Content Markdown links.

Do not scatter unrelated target="_blank" checks across components if a shared helper/component can own the behavior.

External-link rules

External examples:

  • GitHub repositories / organization pages
  • project websites hosted on other domains
  • Code for Germany
  • OpenStreetMap
  • Mastodon profile links that already exist elsewhere on the site
  • source URLs and other external resources

Required output:

<a href="https://example.org" target="_blank" rel="noopener noreferrer">…</a>

Internal examples:

  • /projekte
  • /mitmachen
  • localized Nuxt routes
  • https://oklabflensburg.de/projekte

These must remain same-tab navigation.

Preserve special rel tokens where semantically required, e.g. Mastodon identity verification via rel="me"; combine them with noopener noreferrer rather than replacing them.

Mastodon feed rollback / out of scope

Remove all code that was added solely to show latest Mastodon posts on the homepage. In particular, PR #37 should no longer contain the Mastodon feed feature, its API/client plumbing, cache, fixtures, feed-specific UI, docs, configuration or dependencies.

Expected removals include, where they exist only for that feature:

  • app/components/MastodonFeed.vue
  • app/components/MastodonPostCard.vue
  • server/api/social/mastodon.get.ts
  • server/utils/mastodon.ts
  • shared/types/mastodon.ts
  • docs/mastodon.md
  • Mastodon feed-specific tests / fixtures / screenshots
  • Mastodon feed-specific env/runtime config
  • dependencies added only for Mastodon parsing/testing
  • homepage feed layout/copy added only for the feed

The existing normal profile link to https://norden.social/@oklabflensburg may remain as an ordinary external social link and should follow the new external-link rule.

Do not add a Mastodon API feed, server-side fetcher, embed, cache, media proxy or homepage social-post section as part of this issue.

Repository-wide audit

At minimum review links in:

  • app/components/content/ProseA.vue
  • AppHeader.vue
  • AppFooter.vue
  • EditorialPage.vue
  • EventCard.vue
  • MeetingSection.vue
  • TeamList.vue
  • LegalDetails.vue
  • error page
  • project detail pages
  • project cards / blog cards if they render external URLs
  • source / GitHub / project website links

Internal Nuxt navigation should continue to use NuxtLink / locale-aware routing.

Accessibility / security

  • never emit target="_blank" without noopener noreferrer
  • keep accessible link names meaningful
  • if a reusable accessible hint for new-tab behavior already exists and is unobtrusive, it may remain
  • do not regress keyboard navigation or focus handling
  • preserve rel="me" where needed for identity verification

Tests

Keep/add focused regression tests for the link behavior only:

  • external Markdown link => _blank + noopener noreferrer
  • internal Markdown link => same tab / Nuxt navigation
  • same-site absolute URL => internal
  • external GitHub / project / source link => _blank
  • query strings and fragments preserved
  • existing rel="me" is preserved alongside safe rel values
  • DE/DA/EN navigation still works

Remove tests that only exist for the Mastodon feed/API/content parsing/cache/fallback.

Acceptance criteria

  • All external HTTP(S) links consistently open in a new tab.
  • Every _blank link includes noopener noreferrer.
  • Existing semantic rel values such as me are preserved where required.
  • Internal links and same-origin absolute links remain same-tab.
  • Nuxt Content links use the same central rule.
  • Localized internal routing, queries and fragments are preserved.
  • No duplicate external-link detection logic is introduced.
  • Mastodon feed/API/cache/components/config/docs/tests/fixtures/screenshots introduced by PR refactor(links): standardize external link behavior #37 are removed.
  • The normal Mastodon profile link may remain as a regular external social link.
  • No new Mastodon feed functionality remains anywhere in the branch.
  • Focused link regression tests pass.
  • pnpm lint, pnpm typecheck, pnpm test, pnpm build and relevant E2E tests pass.
  • Website CI and CodeQL are green on the revised PR.

Activity

  1. self-assigned this
    on Sep 13, 2026
  2. changed the title [-]feat(social): show latest Mastodon posts on homepage[/-] [+]refactor(links): standardize external links site-wide[/+] on Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions