Build, preview, and validate the Uno documentation website (DocFX) locally — surface real content errors vs expected noise, drive rendered pages with Playwright, and validate external-doc commit bumps in import_external_docs.ps1 before a PR. Use when working under doc/, editing articles/** markdown, bumping an external docs ref (uno.themes, studio-docs, hd-docs, uno.chefs, workshops, …), or rendering/validating the docs site.
78
98%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Validate that the documentation is correct after a change: surface DocFX errors/warnings, drive
the rendered site in a real browser, and tell real content problems apart from expected noise.
Everything is under doc/ and is independent of the Uno.UI build — no
crosstargeting_override.props needed.
cd doc
docfx --version # MUST match build/Uno.UI.Build.csproj <DocfxVersion>; see Gotchas
pwsh -NoProfile -Command "./import_external_docs.ps1" # only if you changed external pins
docfx docfx.json 2>&1 | sed -E 's/\x1b\[[0-9;]*m//g' > /tmp/docfx.log # capture clean (no color) log
grep -iE "error|warning" /tmp/docfx.log | sort | uniq -c | sort -rn # triageThen filter to the part you changed (§2), drive the rendered pages in a browser (§3), and confirm
they exist in _site.
DocFX prints error and warning lines. The ones that indicate a real content problem you
must fix (and that block/break a page):
| Signal | Meaning | How to fix |
|---|---|---|
InvalidFileLink: Invalid file link:(~/…) | A relative link to a .md/image/path that doesn't exist | Fix the link target or add the missing file. Watch for links to .html that should point to .md, and links missing an extension. |
UidNotFound: … invalid cross reference(s) "X" | An xref:X / @X / <xref> points to a UID no page defines | Fix the UID, or ensure the page defining it is included in the build. Common after renaming a uid: or removing a page. |
InvalidTocInclude: Referenced TOC file … does not exist | A toc.yml href: includes another toc.yml that isn't there | Fix the path, or import the external repo that provides it. (articles/implemented/toc.yml missing is expected locally — see §4.) |
DuplicateUids: Uid(X) has already been defined in … | Two pages declare the same uid: | Make UIDs unique; frequent when copying templates across workshops module variants. |
invalid-tab-group: Tab group with different tab id set | Inconsistent # [Tab](#tab/…) ids within one group | Align the tab ids across the tab group. |
Build does not reach Build succeeded / non-zero exit | A fatal error aborted the build | Read the last error in the log; fix before anything else. |
The change you make should not introduce any new line of these types referencing your files.
The site has a baseline of pre-existing warnings unrelated to your change. The reliable method is to scope the output to what you touched, not to read the whole log.
studio-docs): after import_external_docs.ps1, expect
zero issues mentioning that repo:
grep -i "external/<repo>" /tmp/docfx.log # expect: no error/warning lines
find _site/articles/external/<repo>/ -name "*.html" # expect: the pages rendered_site/….master (or before your change),
then diff the two triaged lists. Only lines new to your branch matter.A clean DocFX log proves the pages built; Playwright proves they render. After serving the
site (dotnet-serve --directory _site --port 8088, or npm start on :3000), drive the pages you
changed and assert on real browser behavior. Use the Playwright MCP browser tools if available, or
npx playwright. For each changed/affected page, check:
<h1>/title is present (not a DocFX 404 or empty body).<img> has naturalWidth > 0.page.on('console') / the MCP console-messages tool).Minimal example (adapt the URL to your changed page):
const { chromium } = require('playwright'); // or use the Playwright MCP browser tools
const page = await (await chromium.launch()).newPage();
const errors = [];
page.on('console', m => m.type() === 'error' && errors.push(m.text()));
const resp = await page.goto('http://localhost:8088/articles/external/studio-docs/App/uno-platform-studio-get-started.html');
console.assert(resp.status() === 200, 'page did not load');
console.assert(await page.locator('h1').count() > 0, 'no heading rendered');
const broken = await page.$$eval('img', imgs => imgs.filter(i => !i.naturalWidth).map(i => i.src));
console.assert(broken.length === 0, 'broken images: ' + broken);
console.assert(errors.length === 0, 'console errors: ' + errors);
await page.screenshot({ path: '/tmp/doc-page.png', fullPage: true });Report findings with explicit labels: Build (DocFX log clean) vs Runtime (Playwright loaded the page and asserted). Don't present a clean build as runtime proof.
These appear on a clean checkout and are not caused by your change:
InvalidTocInclude … articles/implemented/toc.yml does not exist and an empty/placeholder
implemented TOC — the API "implemented views" are generated by CI only.InvalidFileLink/UidNotFound/DuplicateUids already present in unrelated areas (notably
workshops, silverlight-migration, feature includes). Confirm they don't reference your files.ApplyTemplateRendererError … maximum recursion limit of 256 when it hits every toc.yml
→ this is a DocFX version bug (newer versions), not content. Pin DocFX to the CI version
(Gotcha #1) and it disappears.<DocfxVersion> from build/Uno.UI.Build.csproj. The
default dotnet tool update --global docfx installs the latest, which can emit the false
recursion limit of 256 on every TOC (see §4).
docfx --version
dotnet tool uninstall --global docfx
dotnet tool install --global docfx --version <DocfxVersion>import_external_docs_test.ps1. That wrapper forces
each repo to its branch (studio-docs = "main", …), so it ignores the ref= you committed.
Run import_external_docs.ps1 directly (no args) to honor the committed pins.CI source of truth: build/ci/docs/.azure-devops-docs.yml → MSBuild target GenerateDoc in
build/Uno.UI.Build.csproj runs (≈ lines 111-134): sync-generator (skip locally) →
import_external_docs.ps1 → generate-llms-full.ps1 → install pinned DocFX → docfx docfx.json.
Styles/scripts come from npm install && npm run build (= gulp build, which also runs DocFX),
output to doc/_site.
To preview with styles: npm install then npm run build, then
dotnet-serve --directory _site --port 8088 (or npm start to build + live-serve on :3000).
Generated/gitignored — never commit: doc/_site, doc/styles, doc/node_modules,
doc/articles/external/**. Only the ref= pins in import_external_docs.ps1 are committed.
For markdown style there are also CI lint checks you can run locally:
cspell --config ./cSpell.json "doc/**/*.md" --no-progress and markdownlint "doc/**/*.md"
(see doc/README.md).
unoplatform/uno
82b7598
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.