Skip to main content

Style properties reference

Every Markdown element is styled by naming it in a MarkdownTheme { } builder and chaining modifiers onto it. This page lists each element and the modifiers it takes. For how a theme reaches a view and how layering works, see MarkdownTheme.

The builder​

A theme is a set of overrides. Name only the elements you want to change; everything else keeps the default listed below.

let appTheme = MarkdownTheme {
Paragraph()
.font(.body)
.lineHeight(26)

Heading(1).foregroundStyle(.primary)

CodeBlock()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
}

The elements:

ElementApplies to
Paragraph()Body text
Heading(1) … Heading(6)Headings, each level styled independently
Blockquote()Block quotes, and the geometry of admonitions
Admonition(.note)One GitHub alert type's colors
List()Ordered and unordered lists, including their markers
TaskList()Task list checkboxes
Table()GFM tables
CodeBlock()Fenced code blocks
Code()Inline code
Link()Links and autolinks
Strong()Bold text
Emphasis()Italic text
Strikethrough()Struck-through text
Underline()Underlined text (needs MarkdownParsingOptions(underline: true))
Superscript() / Subscript()Raised and lowered text (needs the matching option)
Highlight()Highlighted text (needs MarkdownParsingOptions(highlight: true))
Spoiler()The overlay concealing ||spoiler|| text
BlockImage()Images alone in a paragraph
InlineImage()Images sharing a line with text
ThematicBreak()Horizontal rules
SyntaxToken(_:)One token type's color in a highlighted code block (needs the EnrichedMarkdownSyntaxHighlighting product)

MathBlock() and InlineMath() come with the EnrichedMarkdownLaTeX product - see LaTeX math.

Shared modifiers​

The elements that carry text - Paragraph, Heading, Blockquote, List, Table, CodeBlock, Code, Link, Strong, Emphasis, Strikethrough, Underline, Highlight - accept this base set. Every one of them compiles, but not every element reads every modifier; see which modifiers each element honors below the table.

ModifierEffect
.font(_ font: Font)A SwiftUI text style, in any spelling (.body, .system(.title), .system(.title, design: .serif, weight: .bold)). Scales with Dynamic Type
.font(size: CGFloat, weight: Font.Weight = .regular, design: Font.Design? = nil)A fixed system size
.font(custom name: String, size: CGFloat)A registered font family at a fixed size
.bold()Bold weight, over whatever font a lower layer set. Shorthand for .fontWeight(.bold)
.fontWeight(_ weight: Font.Weight)Any weight, over whatever font a lower layer set. On a custom family, only .semibold and heavier do anything - they switch to the family's bold face
.italic(_ isActive: Bool = true)The family's italic face, or a synthesized slant when it has none. .italic(false) removes an italic a lower layer set
.fontDesign(_ design: Font.Design).default, .serif, .rounded, .monospaced - system fonts only
.foregroundStyle(_ color: Color)Text color
.foregroundStyle(_ semantic:).primary, .secondary, .tertiary, .quaternary, .tint - adapt to light and dark
.marginTop(_ value: CGFloat)Space above the block
.marginBottom(_ value: CGFloat)Space below the block
.lineHeight(_ value: CGFloat)Line height in points
.multilineTextAlignment(_ alignment: TextAlignment).leading, .center, .trailing
MarkdownTheme {
Paragraph()
.font(.body)
.foregroundStyle(.primary)
.lineHeight(26)
.marginBottom(16)

Heading(2)
.font(custom: "Inter-SemiBold", size: 24) // fixed size, no Dynamic Type
.multilineTextAlignment(.leading)
}

.bold(), .fontWeight(_:), .italic(_:), and .fontDesign(_:) layer over the font a lower theme layer set, so Heading(1).bold() on its own bolds the default heading font rather than replacing it.

Which modifiers each element honors​

The base set is shared through one protocol, so it compiles on every text element - but a renderer reads only what that element can use:

ElementFont modifiersColorMargins, line height.multilineTextAlignment
Paragraph, Heading, CodeBlockYesYesYesYes
Blockquote, ListYesYesYesIgnored
TableYes - see Table() for the sizeYesYesIgnored - use .alignment(_:)
CodeYes - any font pins inline code, see Code()YesIgnoredIgnored
Link, Strong, Emphasis, Strikethrough, Underline, HighlightIgnoredYesIgnoredIgnored
caution

On Link, Strong, Emphasis, Strikethrough, Underline, and Highlight, the font modifiers - .font(_:), .font(size:weight:design:), .font(custom:size:), .bold(), .fontWeight(_:), .italic(_:), .fontDesign(_:) - compile and do nothing. These elements always render in the font of the block they sit in, bolded or slanted by the renderer where the element calls for it. Change the block's font instead.

The elements that are not text blocks take their own modifiers instead, listed in their sections below: TaskList(), Admonition(), Spoiler(), BlockImage(), InlineImage(), ThematicBreak(), Superscript() / Subscript(), and SyntaxToken(_:).

Background modifiers - .background(_:) and its alias .backgroundStyle(_:) - are available on Code, CodeBlock, Blockquote, Admonition, Highlight, and Spoiler only; a paragraph or heading has no fill.

caution

.font(size:weight:design:) and .font(custom:size:) take a point size, which opts the element out of Dynamic Type. Use .font(.body) and its siblings unless you have a reason not to; see Custom fonts.

note

.font(_:) reads back a SwiftUI text style only. A point-sized or otherwise modified Font - .system(size: 17), .custom(_:size:), .weight(), .italic() - cannot be inspected through public API, so passing one logs a runtime warning and renders as .body. Use .font(size:weight:design:) and .font(custom:size:) for those.

Style inheritance​

Block elements are self-contained: Paragraph() and Heading(1) each carry their own font, color, line height, and margins, and one never falls back to the other.

Inline elements are partial by design. Strong, Emphasis, Strikethrough, Underline, Code, and Link set only what makes them distinct, and everything else - size, line height, and by default the color - comes from whichever block the text sits in. Bold text in a heading is therefore heading-sized; bold text in a paragraph is paragraph-sized, with no configuration.

That is why several inline modifiers below are documented as inherited rather than as a value. Setting one pins it everywhere the element appears:

MarkdownTheme {
Strong().foregroundStyle(.red) // bold is red in headings, paragraphs, quotes alike
}

Leave it unset to keep bold following its surroundings.

Dark mode and Dynamic Type​

MarkdownTheme.default is built from semantic colors and system text styles, so it adapts to both the appearance and the text size on its own. A theme you write adapts only as far as the values you give it do:

You writeWhat adapts
.font(.body), .font(.largeTitle), any SwiftUI text styleSize follows Dynamic Type
.font(size: 17), .font(custom: "Inter", size: 17)Nothing - a fixed point size
.foregroundStyle(.primary), .secondary, .tint, .quaternaryLight and dark, automatically
.foregroundStyle(Color(.label)), or any dynamic system ColorLight and dark, automatically
.foregroundStyle(Color(red: …, green: …, blue: …))Nothing - a fixed color

Values resolve when a view renders, not when the theme is built, so a theme hoisted to a let at file scope adapts exactly as well as one rebuilt in body. For scaling a custom family along with the text size, see Custom fonts.

Capping or disabling text scaling​

There is no font-scaling option on the view. EnrichedMarkdownText reads dynamicTypeSize from the environment and resolves against it, so SwiftUI's own modifier is the control - applied to the view or anywhere above it:

EnrichedMarkdownText(content)
.dynamicTypeSize(...DynamicTypeSize.accessibility1) // cap how far it scales

EnrichedMarkdownText(content)
.dynamicTypeSize(.large) // pin one size

Capping is almost always the better of the two: it keeps the document readable at large text sizes without letting a long heading run off the screen.

Property reference​

Each element below lists the modifiers it adds on top of the shared modifiers, or - where it takes none of those - its whole surface. A default of unset means the property falls through to whatever a lower theme layer set, or to the renderer's own fallback.

Paragraph()​

Body text. Takes the shared modifiers and adds none of its own; no background.

ModifierTypeDefaultDescription
.font(_:)Font.bodyText style for the block
.foregroundStyle(_:)Color | semantic.primaryText color
.lineHeight(_:)CGFloat26Line height in points
.marginTop(_:)CGFloatunsetSpace above the block
.marginBottom(_:)CGFloat16Space below the block
.multilineTextAlignment(_:)TextAlignmentunset.leading, .center, or .trailing
MarkdownTheme {
Paragraph()
.font(.body)
.lineHeight(28)
.marginBottom(20)
}

Heading(_ level: Int)​

One heading level, 1 through 6. The level is clamped into that range, so Heading(9) styles h6. Every level takes the same shared modifiers and only the defaults differ; no background.

LevelFontColorMargin bottom
Heading(1).largeTitle, bold.primary8
Heading(2).title, bold.primary8
Heading(3).title2, bold.primary8
Heading(4).title3, bold.primary8
Heading(5).headline.primary8
Heading(6).subheadline.secondary8
MarkdownTheme {
Heading(1)
.font(.largeTitle)
.bold()
.marginTop(24)
.marginBottom(12)

Heading(2).font(.title2).foregroundStyle(.secondary)
}

Blockquote()​

Block quotes, and the geometry every admonition inherits. Takes the shared modifiers plus:

ModifierTypeDefaultDescription
.border(_:width:)Color | semantic, CGFloat?.tint, 3Color of the accent bar down the side, and optionally its thickness. An Admonition() tint overrides the color for that alert type
.border(width:)CGFloat3Thickness alone, keeping the color a lower layer set
.gapWidth(_:)CGFloat16Space between the bar and the quoted text
.background(_:)Color | semantic#FAFAFCFill behind the quote. Left unset, the renderer draws a fixed near-white #FAFAFC that does not adapt to dark mode
MarkdownTheme {
Blockquote()
.border(.secondary, width: 4)
.gapWidth(12)
.background(Color(.secondarySystemBackground))
}

Font defaults to .body, color to .secondary, and margin bottom to 16. Nested quotes each draw their own bar, so depth stays visible.

caution

The default quote fill is a fixed light color, so in dark mode a quote sits on a near-white box. If your app supports dark mode, set .background(_:) to an adaptive color such as Color(.secondarySystemBackground) - or .clear for no fill.

Admonition(_ type: AdmonitionType)​

Colors for one GitHub alert type. Geometry, font, and spacing come from Blockquote() - an admonition only recolors it, so these two modifiers are the whole surface:

ModifierTypeDefaultDescription
.foregroundStyle(_:)Color | semanticper type, see belowTints the accent bar, the icon, and the title together
.background(_:)Color | semanticunset - no fillFill behind the callout. An admonition does not inherit the Blockquote() background: unset means no fill
MarkdownTheme {
Admonition(.warning)
.foregroundStyle(.orange)
.background(Color.orange.opacity(0.12))

Admonition(.note)
.foregroundStyle(.blue)
}

The five types and their default tints, GitHub's palette:

TypeMarkerTint
.note> [!NOTE]#0969DA
.tip> [!TIP]#1A7F37
.important> [!IMPORTANT]#8250DF
.warning> [!WARNING]#9A6700
.caution> [!CAUTION]#CF222E

A type you never style falls back to the blockquote's border color. Admonitions need MarkdownParsingOptions(admonitions: true); see Parser extensions.

List()​

Ordered and unordered lists, and their markers. Takes the shared modifiers - which style the item text - plus:

ModifierTypeDefaultDescription
.bulletColor(_:)Color | semantic.secondaryThe dot drawn for an unordered item
.bulletSize(_:)CGFloat6Diameter of that dot, in points
.markerColor(_:)Color | semantic.secondaryThe 1. 2. 3. of an ordered item
.markerMinWidth(_:)CGFloat0Minimum width reserved for the marker column. An ordered list's column also grows to fit its widest number, so its markers are never clipped
.gapWidth(_:)CGFloat12Space between the marker and the item text. Clamped to a minimum of 4
.marginLeading(_:)CGFloat24Indent added per nesting level
MarkdownTheme {
List()
.bulletColor(.tint)
.bulletSize(8)
.markerColor(.tint)
.markerMinWidth(24) // keeps text aligned past "9."
.gapWidth(10)
.marginLeading(20)
}

Font defaults to .body, color to .primary, and margin bottom to 16.

TaskList()​

The checkboxes of - [ ] / - [x] items, and what a checked item's text looks like. Item text itself is styled by List(), so this element takes only these modifiers - no font, no margins:

ModifierTypeDefaultDescription
.checkboxSize(_:)CGFloat14Side length of the box, in points
.checkboxCornerRadius(_:)CGFloat3Corner radius of the box
.checkedColor(_:)Color | semantic.tintFill of a checked box
.borderColor(_:)Color | semantic.secondaryOutline of an unchecked box
.checkmarkColor(_:)Color | semantic.whiteThe tick inside a checked box
.checkedTextColor(_:)Color | semanticunsetRecolors the text of a checked item - the usual "done, dimmed" treatment. Unset leaves it the same as an unchecked item
.checkedStrikethrough(_ enabled: Bool = true)BoolfalseStrikes through the text of a checked item
MarkdownTheme {
TaskList()
.checkboxSize(18)
.checkboxCornerRadius(9) // a circle, at half the size
.checkedColor(.green)
.checkedTextColor(.secondary)
.checkedStrikethrough()
}

The last two apply the moment the reader toggles a box, not only on the first render.

Table()​

GFM tables. Takes the shared modifiers for the cell text, plus:

ModifierTypeDefaultDescription
.headerFont(custom name: String, size: CGFloat)String, CGFloatthe cell fontA registered family at a fixed size, for the header row alone
.headerFont(_:)Fontthe cell fontA SwiftUI text style for the header row alone
.headerForegroundStyle(_:)Color | semantic.primaryHeader row text color
.headerBackground(_:)Color | semantictertiarySystemFillHeader row fill
.rowOddBackground(_:) / .rowEvenBackground(_:)Color | semanticquaternarySystemFill / unsetZebra striping for body rows
.border(_:width:)Color | semantic, CGFloat?separator, 1Color of the grid and outer border, and optionally its thickness
.border(width:)CGFloat1Thickness alone, keeping the color a lower layer set
.cornerRadius(_:)CGFloat6Rounds the outer border
.cellPadding(horizontal:vertical:)CGFloat?, CGFloat?12 / 8Inset inside every cell; a side you omit keeps what a lower layer set
.alignment(_ value: HorizontalAlignment)HorizontalAlignment.leadingWhere a table narrower than the text sits. .leading, .center, .trailing
MarkdownTheme {
Table()
.headerForegroundStyle(.primary)
.headerBackground(Color(.systemGray5))
.rowOddBackground(Color(.systemGray6))
.border(Color(.separator), width: 1)
.cornerRadius(8)
.cellPadding(horizontal: 14, vertical: 10)
.alignment(.center)
}

Line height defaults to 20, cell color to .primary, and margin bottom to 16.

The cell font has no text-style default. Left unset, cells use the paragraph's face at a fixed 14 pt, so default table text does not scale with Dynamic Type; set .font(_:) with a text style to make it scale. Because there is no cell font until you set one, .bold(), .fontWeight(_:), .italic(_:), and .fontDesign(_:) on Table() only take effect alongside a .font modifier in the same or a lower layer. The header row is always bold: the renderer bolds whatever header font it ends up with - .headerFont(_:), or the cell font - and there is no modifier to turn that off.

note

.alignment(_:) places the whole table when it is narrower than the surrounding text - it is not column alignment, which comes from the Markdown separator row alone. It takes SwiftUI's HorizontalAlignment, but only .leading, .center, and .trailing mean anything: any other alignment logs a warning and leaves the inherited value. A table wider than the text scrolls horizontally and ignores this. Table() also accepts the shared .multilineTextAlignment(_:) because it shares the base element protocol, but nothing reads it.

CodeBlock()​

Fenced code blocks. Takes the shared modifiers - including .bold(), .fontWeight(_:), .italic(_:), .fontDesign(_:), and .multilineTextAlignment(_:) - plus the block-specific ones below:

ModifierTypeDefaultDescription
.font(_:)Font.system(.body, design: .monospaced)Text style for the code
.font(custom name:size:)String, CGFloatunsetA registered family at a fixed size
.font(size:weight:design:)CGFloat, Font.Weight, Font.Design?unsetA fixed system size
.foregroundStyle(_:)Color | semantic.primaryCode color
.background(_:)Color | semantic.quaternaryFill behind the block
.padding(_:)CGFloat12Space between the fill's edge and the code
.cornerRadius(_:)CGFloat8Rounds the fill
.border(_:width:)Color | semantic, CGFloat?#38404A, 1Outline around the fill, and optionally its thickness. Left unset, the renderer draws a fixed dark-gray 1 pt outline
.border(width:)CGFloat1Thickness alone, keeping the color a lower layer set. 0 removes the outline
.lineHeight(_:)CGFloatunsetLine height in points
.marginTop(_:) / .marginBottom(_:)CGFloatunset / 16Space above and below
.headerForegroundStyle(_:)Color | semanticunsetLanguage label and copy button of a scrollable panel. Unset, they take the code color at 60% opacity
MarkdownTheme {
CodeBlock()
.font(custom: "JetBrainsMono-Regular", size: 13)
.background(Color(.secondarySystemBackground))
.border(Color(.separator), width: 1)
.cornerRadius(10)
.padding(14)
}

A block keeps the monospaced design unless you replace it explicitly: .font(.body) re-sizes the monospaced face, while .font(.system(.body, design: .serif)) or .font(custom:size:) gives you the face you named.

The default outline is a fixed color that does not adapt to dark mode. To match your palette, set .border(_:width:) yourself, or .border(width: 0) to drop it.

The scrollable panel's header font is not themable: the language label is always the system font, medium weight, at 85% of the code font's size.

Code is drawn in a single color by default. To color it by the fence's language, add the EnrichedMarkdownSyntaxHighlighting product - see Syntax highlighting - and style the token colors with SyntaxToken(_:).

Code()​

Inline `code`. Takes the shared modifiers plus .background(_:):

ModifierTypeDefaultDescription
.fontDesign(_:)Font.Design.monospacedKept at the size of the surrounding text
.foregroundStyle(_:)Color | semantic.secondaryText color
.background(_:)Color | semantic.quaternaryThe chip behind the code
MarkdownTheme {
Code()
.foregroundStyle(.pink)
.background(Color.pink.opacity(0.12))
}

Leaving the font unset is what keeps inline code the size of the line it sits in - inline code in a heading is heading-sized. In that case it is always the system monospaced face at the line's size, never the paragraph's family. Passing any font - .font(.body), .font(size:weight:design:), or .font(custom:size:) - replaces that with exactly the font you gave, pinned to that size everywhere. For the same reason, .bold(), .fontWeight(_:), .italic(_:), and .fontDesign(_:) on Code() do nothing without a .font modifier - including the default .fontDesign(.monospaced), which is listed for completeness: the monospaced face comes from the renderer.

Links and autolinks. Takes the shared modifiers plus:

ModifierTypeDefaultDescription
.underline(_ enabled: Bool = true)BooltrueWhether the link is underlined
.foregroundStyle(_:)Color | semantic.tintLink color; everything else is inherited from the surrounding block
MarkdownTheme {
Link()
.foregroundStyle(.blue)
.underline(false)
}

A tapped link goes through the openURL environment action, which opens it with the system unless you install your own.

Strong(), Emphasis(), Strikethrough()​

Bold, italic, and struck-through text. Each takes the shared modifiers, and each sets nothing by default: the weight, slant, or line comes from the renderer and everything else is inherited.

MarkdownTheme {
Strong().foregroundStyle(.primary)
Emphasis().foregroundStyle(.secondary)
Strikethrough().foregroundStyle(.tertiary)
}

Setting a color on one pins it document-wide, including inside headings and quotes.

Underline()​

Underlined text, from _text_ and __text__ with MarkdownParsingOptions(underline: true). Takes the shared modifiers; nothing set by default.

EnrichedMarkdownText(content, options: MarkdownParsingOptions(underline: true))
.markdownTheme {
Underline().foregroundStyle(.tint)
}
note

The option replaces the usual meaning of those markers. With it on, _text_ is underlined rather than italic - use *text* and **text** for italic and bold.

Superscript() / Subscript()​

Raised and lowered text, from ^text^ and ~text~ with the matching option. Both scale relative to the text around them, so these are the only two modifiers - font and color follow the surrounding text:

ModifierTypeDefaultDescription
.fontScale(_:)CGFloat0.75Size as a fraction of the surrounding text size
.baselineOffsetScale(_:)CGFloat0.35 superscript, 0.20 subscriptBaseline shift as a fraction of the surrounding text size - upward for Superscript, downward for Subscript. Pass a positive number for both
MarkdownTheme {
Superscript()
.fontScale(0.7)
.baselineOffsetScale(0.4)

Subscript()
.fontScale(0.7)
.baselineOffsetScale(0.25)
}

Because the values are fractions, superscript inside an h1 is larger than superscript in a paragraph, automatically.

Highlight()​

==text== with MarkdownParsingOptions(highlight: true). Takes the shared modifiers plus:

ModifierTypeDefaultDescription
.background(_:)Color | semantic#FEF08AThe highlight behind the text
MarkdownTheme {
Highlight()
.background(Color.yellow.opacity(0.3))
.foregroundStyle(.primary) // set both, so dark mode stays readable
}
caution

The default highlight background is a fixed light yellow and does not adapt to dark mode, where the surrounding text color may leave it unreadable. If you enable the option, set both a background and a foreground for the appearance you support.

Spoiler()​

The overlay concealing ||spoiler|| text until it is tapped. Spoilers are always parsed; the overlay's shape is chosen with .markdownSpoilerOverlay. Once revealed, the text keeps the surrounding font and color, so this element styles the cover only:

ModifierTypeDefaultDescription
.foregroundStyle(_:)Color | semantic.secondaryThe particles, or the fill of the solid box
.background(_:)Color | semanticsystemBackgroundBackdrop painted under the particles, hiding the text through the gaps. The solid overlay ignores it
EnrichedMarkdownText(content)
.markdownSpoilerOverlay(.particles(density: 16, speed: 30))
.markdownTheme {
Spoiler()
.foregroundStyle(.secondary)
}

The theme sets the spoiler's colors; how the overlay behaves is tuned on the overlay itself. Density and speed are arguments of .markdownSpoilerOverlay(.particles(density:speed:)), in absolute units - particles per 100×100 pt and points per second - not multipliers: density: 16 is twice as dense as the default 8, and speed: 40 drifts twice as fast as the default 20. The solid overlay's corner radius is an argument of .solid(cornerRadius:) and is an absolute value in points, 4 by default.

For an overlay of your own design, see Custom spoiler overlays; it receives the same Spoiler() colors.

BlockImage()​

An image alone in its paragraph. No font or color - it is sized, rounded, and spaced:

ModifierTypeDefaultDescription
.height(_:)CGFloat200A fixed box height in points; the box spans the container width
.maxHeight(_:)CGFloatunsetThe image's own proportions at the available width, capped at this height
.aspectRatio(_:)CGFloat or CGSizeunsetSizes the box from the available width and a width-over-height ratio, e.g. 16 / 9
.aspectRatio(_:contentMode:)CGFloat, ImageContentModeunsetBoth at once - the ratio shapes the box, the mode places the image in it
.contentMode(_:)ImageContentModesee belowHow the bitmap fills that box
.cornerRadius(_:)CGFloat8Rounds the image
.marginTop(_:)CGFloatunsetOverrides the paragraph margin above an image-only paragraph
.marginBottom(_:)CGFloat16Overrides the paragraph margin below it
MarkdownTheme {
BlockImage()
.aspectRatio(16 / 9, contentMode: .fill)
.cornerRadius(12)
.marginBottom(24)
}

The box is always as wide as the available width - there is no width modifier - so the sizing modifiers decide only its height:

  • .height(_:) - a fixed height, whatever the image's proportions. This is what the default theme sets, at 200.
  • .maxHeight(_:) - the height that keeps the image's own proportions at the available width, capped at the value. Until the image has loaded, the box stands at the cap.
  • .aspectRatio(_:) - the available width divided by the ratio, so 16 / 9 gives a widescreen box. The CGSize overload takes width / height of the size you pass.

The three are mutually exclusive: a theme sets exactly one, the last one applied wins, and a layer that sets any sizing replaces the lower layer's - a .maxHeight(400) layered over the default .height(200) leaves no fixed height behind.

.contentMode(_:) places the bitmap inside that box. It takes an ImageContentMode:

CaseDraws the image
.fitScaled to fit inside the box, keeping its proportions. Never cropped
.fillScaled to fill the box, keeping its proportions. Overflow is cropped
.stretchStretched to fill the box exactly, ignoring its proportions
.scaleDownCentered at its own size, scaled down only when it is larger than the box
.originalCentered at its own size, never scaled. Overflow is cropped
.fitWidthScaled to the box's width and centered vertically. Overflow is cropped

Left unset it follows the sizing: a fixed height box draws .fitWidth, and a maxHeight or aspectRatio box .fill. Unlike SwiftUI's .aspectRatio(_:contentMode:), the ratio here shapes the box and the mode places the image in it.

InlineImage()​

An image sharing a line with text, drawn like an oversized glyph. One modifier:

ModifierTypeDefaultDescription
.size(_:)CGFloat20Side length in points
MarkdownTheme {
InlineImage().size(24)
}

Which of the two an image becomes depends on what else is in its paragraph - see Images: block vs. inline.

ThematicBreak()​

The rule drawn for ---. It has no text of its own, so these are all its modifiers:

ModifierTypeDefaultDescription
.foregroundStyle(_:)Color | semantic.secondaryColor of the rule
.height(_:)CGFloat1Thickness of the rule
.marginTop(_:) / .marginBottom(_:)CGFloat24 / 24Space above and below
MarkdownTheme {
ThematicBreak()
.foregroundStyle(Color(.separator))
.height(2)
.marginTop(32)
.marginBottom(32)
}

SyntaxToken(_ type: SyntaxTokenType)​

The color of one token type inside a syntax-highlighted code block. It ships with the EnrichedMarkdownSyntaxHighlighting product, so it needs that import, and it only has an effect where highlighting is turned on with .markdownSyntaxHighlighting() - see Syntax highlighting. One modifier:

ModifierTypeDefaultDescription
.foregroundStyle(_:)Color | semanticper type, see belowColor of every token of that type

Color is the whole surface: a token keeps the CodeBlock() font, and there is no per-token weight, slant, or background.

import EnrichedMarkdown
import EnrichedMarkdownSyntaxHighlighting
import SwiftUI

struct CodeSampleView: View {
let markdown: String

var body: some View {
EnrichedMarkdownText(markdown)
.markdownSyntaxHighlighting()
.markdownTheme {
SyntaxToken(.keyword).foregroundStyle(.pink)
SyntaxToken(.comment).foregroundStyle(.secondary)
}
}
}

SyntaxTokenType has fourteen cases: .keyword, .operator, .punctuation, .string, .number, .constant, .comment, .function, .type, .variable, .property, .tag, .attribute, and .embedded.

Turning highlighting on layers MarkdownTheme.syntaxHighlightingDefault - GitHub's palette - above MarkdownTheme.default and below your own themes, so a SyntaxToken you set overrides one type and leaves the rest. The palette switches between light and dark with the color scheme:

TypeLightDark
.keyword#CF222E#FF7B72
.string#0A3069#A5D6FF
.number, .constant, .property, .attribute#0550AE#79C0FF
.comment#6E7781#8B949E
.function#8250DF#D2A8FF
.type#953800#FFA657
.tag#116329#7EE787

.operator, .punctuation, .variable, and .embedded have no default color, so they keep the code block's color until you give them one.

See also​