Skip to main content
iOS / SwiftUI · WCAG 2.2 AA

Link (Inline)

A hyperlink embedded in a run of Text via an AttributedString .link run (preferred) or Markdown, reachable through the VoiceOver Links rotor but non-operable under Full Keyboard Access and Voice Control because of Apple platform defects.

Selection

The criteria an agent checks before retrieving this component.

Use when

  • Use when a hyperlink must appear inside a running sentence or paragraph, surrounded by static text (e.g., "By continuing you agree to the Terms of Service."). Uses an AttributedString .link run (preferred) or Markdown inside a Text.

Try a different component when

  • Do not use when the link can stand as its own control or on its own line (use link.basic, which is fully operable with Full Keyboard Access and Voice Control).
  • Do not use when the target is an in-app screen rather than a URL (use list.row).
  • Do not use when the control performs an in-app action rather than opening a URL (use button.basic).

Must Haves

Non-negotiable structure. Every generated instance must satisfy these rules.

  • Build the inline link with an AttributedString whose run sets .link to the URL, in preference to Markdown, because only AttributedString can also carry an underline to distinguish the link without color.
  • Distinguish the inline link from surrounding text by more than color: set .underlineStyle on the AttributedString run. Markdown inline links cannot be underlined or restyled, so they cannot satisfy this on their own.
  • The link run text is specific to its destination; avoid generic runs like "here" or "click here".
  • Link text meets 4.5:1 contrast against its background in light and dark appearances (per global.semantic-color); the default inline link tint can fall below 4.5:1.
  • Provide a fully operable alternative to the same destination for keyboard and Voice Control users. AttributedString and Markdown inline links are not focusable under Full Keyboard Access and not targetable by Voice Control Show Names or Show Numbers, an Apple platform defect. Supply a standalone link.basic to the same URL, or keep destinations that must be operable out of inline links.
  • Keep each inline link's text meaningful on its own, because VoiceOver users reach inline links through the Links rotor, which lists them by name out of surrounding context.

Donts

Avoid these accessibility and UX barriers.

  • Do not rely on an inline link as the only way to reach a destination that keyboard or Voice Control users must operate; the platform defect leaves it unreachable for them without a standalone alternative.
  • Do not distinguish an inline link by color alone; set an underline via AttributedString.
  • Do not use Markdown inline links when conformance depends on distinguishing the link without color, since Markdown links cannot be underlined or given a different text style than the surrounding run.
  • Do not use generic inline link text ("here", "click here", "read more") that is not meaningful in the Links rotor.

Customizable

Alternatives and options that give the AI agent some room to move.

  • The underline is required for distinction, but its exact style (single, thick) is at the engineer's discretion as long as the link stays distinguishable without color.
  • The operable alternative may be a visible standalone link.basic, or a repetition of the destination elsewhere on the screen as a Link, as long as keyboard and Voice Control users can reach the same URL.

Golden Pattern

The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.

SWIFT
import SwiftUI

struct InlineLinkDemo: View {
// AttributedString inline link: a settable underline distinguishes it without color alone.
var agreement: AttributedString {
var text = AttributedString("By continuing you agree to the Terms of Service.")
if let range = text.range(of: "Terms of Service") {
text[range].link = URL(string: "https://www.example.com/terms")
text[range].underlineStyle = Text.LineStyle(nsUnderlineStyle: .single)
}
return text
}

var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text(agreement)

// Operable alternative to the same destination for Full Keyboard Access and
// Voice Control users, who cannot reach the inline link above (Apple platform defect).
Link("Read the Terms of Service", destination: URL(string: "https://www.example.com/terms")!)
.accessibilityRemoveTraits(.isButton)
}
}
}

Acceptance Checks

The component’s test spec — an optional body of checks for verification.

Traits & semantics

  • Each inline link is exposed as a link within the text, named by its specific run text.

VoiceOver

  • Inline links are reachable through the VoiceOver Links rotor, each listed by its specific text, and activating one opens the URL.
  • Inline links do not appear as standalone elements in the normal swipe order the way a link.basic control does; the rotor is the reliable path to them.

Switch Control & Full Keyboard Access

  • Inline AttributedString and Markdown links are not focusable or operable via Full Keyboard Access, and are not targetable by Voice Control Show Names or Show Numbers (Apple platform defect).
  • The standalone link.basic alternative to the same destination is reachable and activatable via Switch Control, Full Keyboard Access, and Voice Control.

Dynamic Type

  • The paragraph text and its inline links scale with Dynamic Type and stay fully visible at accessibility text sizes.

Visual

  • Each inline link is distinguishable from the surrounding text by more than color, with an underline present via AttributedString.
  • Inline link text meets 4.5:1 contrast against its background in light and dark appearances.