Skip to content

Fix DocC code-highlighting regressions and add a SwiftSyntax highlighter#1

Merged
Jeehut merged 7 commits into
mainfrom
wip/docc-code-regressions
Jun 18, 2026
Merged

Fix DocC code-highlighting regressions and add a SwiftSyntax highlighter#1
Jeehut merged 7 commits into
mainfrom
wip/docc-code-regressions

Conversation

@Jeehut

@Jeehut Jeehut commented Jun 18, 2026

Copy link
Copy Markdown
Member

Overview

SiteKit's DocC theme had two regressions versus Apple's own DocC rendering, surfaced on wwdcnotes.com: a linked inline code span was no longer distinguishable from a plain one, and fenced-code syntax highlighting collapsed to near-monochrome on strongly saturated accent colours. This branch fixes both, and adds an optional SwiftSyntax-based highlighter for sites that want richer, Xcode-like Swift colouring (green variable references, calls, members).

Everything here lives inside the SiteKit package (core CSS + a new optional product). Consuming sites (e.g. WWDCNotes) only opt in and bump their pin – no fix code lives downstream.

What changes

  1. Linked inline code reads as a link again – a linked <code> renders in the accent colour with an underline, clearly distinct from a plain inline-code pill. (Core docc.css.)
  2. Fixed Apple/Xcode syntax palette (the new default) – token colours were derived from the site accent via color-mix, which collapsed to near-monochrome on a saturated accent (the reported "colour-poor" regression). They are now a fixed Apple/Xcode-style palette (pink keywords, purple types, red strings, blue numbers, …) on a pinned Apple-near-white code surface, scoped to highlighted DocC blocks so non-DocC output is untouched. This applies to the default regex highlighter, so every DocC site gets it. (Core docc.css.)
  3. Optional SwiftSyntax highlighter (SiteKitSyntaxHighlighting product) – the default regex highlighter colours keywords/strings/comments/numbers/attributes/capitalised types. This opt-in highlighter additionally classifies Swift tokens by syntactic role via SwiftSyntax, most visibly colouring variable references green (like Xcode), plus calls/members/params. It lives in its own product/target depending only on SwiftParser / SwiftSyntax / SwiftIDEUtils; a consumer using only the base SiteKit product never compiles swift-syntax. The default stays the regex highlighter – you opt in via SiteBuilder.docc(…, highlighter:).

Design

A CodeHighlighting protocol abstracts the per-snippet highlighter. The zero-dependency regex CodeHighlighter is the default conformer; DocCLoader takes an injected highlighter exposed via SiteBuilder.docc(…, highlighter:). The optional SwiftSyntaxHighlighter uses SwiftIDEUtils' syntactic classification as a base and refines identifiers via a SyntaxVisitor into call / member / variable / param / boolean roles from the parse-tree shape. Non-Swift code falls back to the regex highlighter. Classification is purely syntactic (no symbol graph) – it reads token roles, not type ownership, so all type references share one colour.

In scope / Out of scope

  • In scope: the two regression fixes, the Apple palette (the new default), and the optional SwiftSyntax highlighter – all in the SiteKit package.
  • Out of scope: WWDCNotes-side wiring (the .docc(highlighter:) opt-in + deploy pin) – a consumer change applied after this is released. The version number / changelog – decided at release time, not in this branch. A possible follow-up: bringing some recognition improvements into the default regex highlighter.

Dependency / build cost

swift-syntax (SwiftParser / SwiftSyntax / SwiftIDEUtils only – no macro / compiler-plugin modules) is a dependency of the optional SiteKitSyntaxHighlighting target only. Verified empirically: a consumer depending solely on the base SiteKit product compiles 0 swift-syntax object files (SE-0226 target-based dependency pruning). The build-cost delta (~+30s user-CPU) applies only to sites that opt in.

How I verified

  • swift test: 872 tests / 76 suites green.
  • Red-green proofs on the headline behaviours: removing the variable-classification rule leaves variables uncoloured → restored → green; the Swift Regex block-rewrite was probed by breaking the opening-tag match → tests fail → restored → green.
  • Isolation verified empirically: a scratch consumer depending only on the base SiteKit product builds with 0 swift-syntax object files.
  • em-dash byte-scan clean across the branch.

Notes for reviewers

  • The default look is the regex CodeHighlighter + the Apple palette in Sources/SiteKit/Resources/DocC/docc.css. The opt-in lives in Sources/SiteKitSyntaxHighlighting/; CodeHighlighting.swift is the seam.
  • This addresses the wwdcnotes.com regressions; a consuming DocC site pulls a tagged release to ship the fixes live.

Jeehut added 4 commits June 18, 2026 10:15
A linked inline code span (a class-less <code> inside an <a>) inherited the
neutral inline-code pill, and on themes carrying a bare `code { color: ... }`
rule it also lost the link color outright, so a linked symbol looked identical
to plain code. Add a descendant rule (specificity 0,2,2) that gives the linked
pill the accent color, a visible underline, and an accent-dominant border and
tint, so it reads as a link in every scheme while keeping the pill shape.

The syntax-highlight token colors were all derived from var(--color-accent)
via color-mix, which collapsed to near-monochrome under a strongly saturated
accent: keyword, type, string and number all read as one hue and only comments
stood apart. Decouple the palette into curated, light/dark-aware hues per token
(green strings, teal types, violet numbers, magenta attributes); the keyword
stays the accent as the one brand-tied signal and comments stay muted. Dark
variants flip under [data-theme="dark"], matching the generated token layer.
Every fixed hue is checked for WCAG AA on the code surface in both modes.
The regex highlighter only recognizes keywords, strings, comments,
numbers, attributes, and capitalized type names, so DocC code blocks
read flat: value references, calls, members, and parameters are all
left uncolored. This adds a semantic-near alternative that classifies
Swift tokens by their syntactic role, most visibly turning variable
references green, for an Xcode-like palette.

Introduce a `CodeHighlighting` protocol seam. The zero-dependency regex
`CodeHighlighter` remains the default conformer, and the shared
`applyToBodyHTML` block-rewriting plumbing moves onto the protocol so
every conformer reuses it. `DocCLoader` now holds an injected
highlighter (default regex), and `SiteBuilder.docc(…, highlighter:)`
exposes the injection point.

The SwiftSyntax highlighter lives in its own `SiteKitSyntaxHighlighting`
product and target, depending on only `SwiftParser`, `SwiftSyntax`, and
`SwiftIDEUtils` (not the macro/compiler-plugin modules). Target-based
dependency resolution keeps swift-syntax out of any build that uses only
the base `SiteKit` product, so non-DocC sites pay no added build cost.
It uses the syntactic classification as a base and refines generic
identifiers via a syntax-tree visitor into type, call, variable, member,
param, boolean, and label roles; non-Swift snippets fall back to the
regex highlighter. Classification is purely syntactic, so all type
references share one class (no framework-vs-project split).

Seed core `docc.css` with a fixed Apple/Xcode palette for every role
class in light and dark, and pin the highlighted code-block surface so
the fixed colors keep their WCAG contrast regardless of the site accent.
The SwiftSyntax highlighter colored every capitalized type with the one
purple `type` role, so an SDK type and a project's own type looked alike.
Xcode instead reads SDK types (ScrollView, ForEach) purple and a
project's own types (StickerListItemView, DeleteButton) green.

Approximate that split with a committed framework-type allowlist, since
isolated DocC snippets carry no symbol graph. A capitalized type whose
name is in the allowlist keeps `sk-tok-type`; any other capitalized type
becomes the new `sk-tok-projecttype` role (green). The split is applied
at every point the type role is assigned (the expression visitors for
initializers and bare references, and the base classification for type
annotations), so type-position types are split too.

The allowlist is a committed Swift literal (1755 names) parsed once into
a Set, never re-extracted at build time, so the coloring is reproducible
across machines, CI, and consumers regardless of the installed SDK. It
covers SwiftUI, SwiftUICore, Foundation, Combine, Swift stdlib and
Concurrency, plus curated UIKit, AppKit, and CoreGraphics types so
SwiftUI-interop names like UIView and CGRect read as framework types.

Sites can extend the set with a new non-breaking init parameter,
SwiftSyntaxHighlighter(additionalFrameworkTypes:), to color their own
umbrella or design-system types like the SDK. The core docc.css gains a
placeholder green for the new role in light and dark.
The WWDCNotes documentation site is now live in production at wwdcnotes.com (built with SiteKit), so the README and DocC-blueprint showcase links, the base-URL doc-comment example, and the BaseURLOverride test fixture now use wwdcnotes.com instead of the staging wwdcnotes.fline.dev host.

@Jeehut Jeehut left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that I also wonder what'll happen to non-Swift code in DocC rendered sites like wwdcnotes.com – they should fallback to the other (previous) highlighting logic, no?

Comment thread Sources/SiteKit/Utilities/CodeHighlighting.swift Outdated
Comment thread Sources/SiteKitSyntaxHighlighting/FrameworkTypeAllowlist.swift Outdated
Comment thread Sources/SiteKitSyntaxHighlighting/FrameworkTypeAllowlist.swift Outdated
Comment thread Sources/SiteKitSyntaxHighlighting/FrameworkTypeAllowlist.swift Outdated
Comment thread Tests/SiteKitSyntaxHighlightingTests/HighlighterPreviewGenerator.swift Outdated
Jeehut added 3 commits June 18, 2026 17:37
The optional SwiftSyntax highlighter classified capitalized types against a
committed 1755-name framework allowlist, rendering SDK types purple
(sk-tok-type) and everything else green (sk-tok-projecttype). Resolving every
type through a hand-maintained allowlist is large, error-prone, and drifts with
each SDK bump.

Map every capitalized type (initializer, annotation, or bare reference) to the
single sk-tok-type role, matching the regex highlighter. Delete the allowlist
file, the additionalFrameworkTypes init parameter, and the sk-tok-projecttype
CSS (light and dark). The highlighter keeps the syntactic roles the regex pass
cannot produce, above all the green variable references.
HighlighterPreviewGenerator was scaffolding for eyeballing the token palette
during the highlighter's colour pass: it wrote a self-contained HTML preview
only when an environment variable pointed at an output path. The palette has
landed, so the generator is no longer needed in the test target.
applyToBodyHTML located each <pre><code>...</code></pre> block with an
NSRegularExpression and rebuilt the result by hand: NSString bridging, manual
cursor arithmetic, and NSNotFound group checks.

Swap in a Swift Regex literal with named lang and body captures and drive the
rewrite through String.replacing(_:with:). The optional lang capture is nil
exactly when the class attribute is absent, which folds the NSNotFound and
empty-language cases into one expression and drops the cursor bookkeeping. The
match semantics are unchanged: the existing applyToBodyHTML tests (tagged,
default-language, no-default passthrough, escaping, and the // in a string
case) stay green.
@Jeehut Jeehut merged commit 6834eda into main Jun 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant