Convert a W3C spec from ReSpec (index.html) to Bikeshed (index.bs), keeping its anchors and exported terms stable
77%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./skills/bikeshed-conversion/SKILL.mdConvert the ReSpec-based index.html in the current repo (or $ARGUMENTS if provided) to a Bikeshed index.bs file.
Use the resource-timing spec at ../resource-timing/index.bs (relative to the repo) as a reference for Bikeshed patterns if it exists.
The spec's anchors must survive the move. Other specs, MDN, WPT, and issues link to its ids, and other specs autolink to its exported terms. A straight conversion silently breaks both, as it did for hr-time (w3c/hr-time#173, w3c/hr-time#175). So capture a baseline before changing anything (step 1), keep anchors stable while converting, and verify against the baseline at the end (step 7).
Before changing anything, save the spec as ReSpec renders it. The ReSpec source isn't enough: ReSpec generates most ids (dfn-*, dom-*, section ids) and linking texts while rendering.
curl -sL -o /tmp/<repo>-before.html "https://www.w3.org/publications/spec-generator/?type=respec&url=https://w3c.github.io/<repo>/"
grep -c '<dfn' /tmp/<repo>-before.html # must be more than 0This only works while the live ED is still the ReSpec version you're converting. If the ED has already switched to Bikeshed, use webref's crawl from before the switch instead; check-anchors.py --help (in this skill's directory) explains how.
index.html to understand the full spec structurerespecConfig object (title, editors, shortName, etc.)data-cite cross-spec referencesindex.bsMap respecConfig to a <pre class='metadata'> block:
| ReSpec | Bikeshed |
|---|---|
shortName | Shortname: |
specStatus: "ED" | Status: ED |
editors | Editor: Name, Affiliation URL, personal-url-or-email, w3cid NNNNN |
formerEditors | Former Editor: lines |
group | Group: webperf |
github | Repository: w3c/repo-name |
wgPublicList | Mailing List: + Mailing List Archives: |
testSuiteURI | Test Suite: |
caniuse | Include Can I Use Panels: yes + Can I Use URL: (if needed) |
Also add:
Level: none (unless spec has a level number)Complain About: accidental-2119 yesMarkup Shorthands: markdown noURL: pointing to the ED URLTR: pointing to the TR URL<section id="abstract"> into Abstract: metadataStatus Text: metadata (this appends to auto-generated W3C boilerplate)Drop mdn, xref, doJsonLd, subjectPrefix, highlightVars (no Bikeshed equivalents / handled automatically).
Add <pre class=link-defaults> for cross-spec references that Bikeshed can't auto-resolve. Common ones:
spec:html; type:dfn; for:/; text:global object
spec:html; type:dfn; text:document
spec:html; type:interface; text:SharedWorker
spec:html; type:interface; text:BroadcastChannel
spec:infra; type:dfn; text:user agent
spec:service-workers; type:dfn; text:service worker timing infoAdd <pre class=anchors> for references to specific spec anchors that Bikeshed doesn't know about. Format:
urlPrefix: https://html.spec.whatwg.org/multipage/; spec: HTML
type: dfn
urlPrefix: browsing-the-web.html
text: unloading documents; url: #unloading-documentsConvert localBiblio entries to a <pre class=biblio> JSON block. Make sure every local biblio entry is actually referenced with [[REF-NAME]] somewhere in the document, or Bikeshed will warn about unused entries.
Keep any custom styling as an inline <style> block. Also add this rule so notes display a "Note" title (ReSpec does this automatically, Bikeshed does not):
.note:not(.no-marker)::before {
content: "Note";
}Elements that work identically (no changes needed):
<pre class='idl'> blocks<dfn> with data-dfn-for attributes{{InterfaceName/member}} autolinks (use / not .)[=term=] definition links[[SPEC-NAME]] bibliography refs<p class="note">, <div class="note"><pre class='example'> blocks|variable| algorithm syntax (but NOT |variable:type| — see below)<figure>, <figcaption>, <img>Elements that need conversion:
data-cite="SPEC#anchor" links → Convert to [=term=], {{IDL}}, or <a>text</a> with anchors block<dfn data-cite="SPEC#...">Term</dfn> → Convert to a link (not a <dfn>) since the definition lives externally, and keep its old id on that link (see "Keep anchors and exported terms stable")[[REF|display text]] → <a href="...">display text</a> [[REF]] (Bikeshed doesn't support [[REF|text]])[[?REF]] informative refs → [[REF]] (Bikeshed defaults to informative; use [[!REF]] for normative)data-link-for attributes → for= attributes on <a> elements<section id="abstract"> → Remove (content moved to metadata)<section id="sotd"> → Remove (Bikeshed auto-generates SOTD boilerplate; use Status Text: metadata only for custom additions). Note: Bikeshed's ED boilerplate is shorter than ReSpec's — it omits patent policy text (that only appears for published statuses like WD/CR).<section id="conformance"></section> → Remove (Bikeshed auto-generates), but keep #conformance working (see "Keep anchors and exported terms stable")<section id="index"></section> and <section id="idl-index"></section> → Remove (Bikeshed auto-generates)<script> and class='remove' elements → Remove entirely<section> wrappers → Remove; Bikeshed infers section boundaries from headings. Move each section's id to its heading.data-tests attributes → Keep as-is. Note: Bikeshed passes them through but does NOT render them as visible test links like ReSpec does.Non-normative sections:
class='informative' to <h2> for non-normative sections (Introduction, Privacy, Security)<em>This section is non-normative.</em> after each such headingExamples:
example-1, example-2, ...). To give one a descriptive id, keep the old one with oldids: <div class=example id=example-foo oldids=example-1>highlight='html' or highlight='js' for syntax highlighting on code examplesExported definitions:
data-export to all <dfn> elements that define terms other specs might referenceImages:
width and height attributes to <img> tags to avoid Bikeshed warningsviewBox attribute if it's an SVGAlgorithm variables — CRITICAL (|var:type| is ReSpec-only):
|var:type| syntax (e.g., |timestamp:unsafe moment|) to annotate variable types. Bikeshed does NOT support this — it renders the |var:type| text literally in the output, showing raw pipe characters and colons to readers.|var:type| occurrences to plain |var|. Drop the :type annotation entirely.|var| references that appear only once in their scope. Fix by replacing every single-use |var| with <var ignore>var</var>.bikeshed -f spec and fix ALL remaining single-use var warnings this way.Complex list items and markdown parser conflicts:
<ul>/<li> elements that contain multiple <p> or <div> children, especially when <dfn> attributes span multiple lines. This causes "isn't indented enough to be valid Markdown" FATAL errors, even with Markup Shorthands: markdown no.: term / :: definition) wrapped in a <div>, or (b) flattening multi-paragraph <li> content into simple inline text.<li> elements to standalone <p class="note"> elements after the list.Section wrapping for styling:
<div id="..."> since Bikeshed doesn't create <section> wrappers. Give the <h2> a different explicit id to avoid duplicates.Every id in the baseline must still exist, and every exported term must still be exported with the same type, for, and linking text. Step 7 checks both.
Ids. Bikeshed generates different ids than ReSpec, e.g. #moment instead of #dfn-moment for a concept, and #performance instead of #dom-performance for an interface. Wherever they differ, set the baseline's id explicitly:
<!-- BAD: Bikeshed auto-generates an ID that may differ from the original -->
<dfn data-export>wall clock</dfn>
<!-- GOOD: Explicit ID preserves the anchor -->
<dfn data-export id=dfn-wall-clock>wall clock</dfn>Typical ReSpec id patterns that get lost, from w3c/hr-time#173:
| Type | Pattern | Example |
|---|---|---|
| Concept definitions | dfn-{name} | id=dfn-wall-clock, id=dfn-monotonic-clock, id=dfn-duration |
| Algorithm definitions | dfn-{algorithm-name} | id=dfn-coarsen-time, id=dfn-duration-from |
| IDL typedefs | dom-{typename} | id=dom-domhighrestimestamp, id=dom-epochtimestamp |
| IDL interfaces | dom-{interface} | id=dom-performance |
| IDL blocks | idl-def-{name} | id=idl-def-domhighrestimestamp |
| Compound concepts | dfn-{hyphenated} | id=dfn-current-high-resolution-time, id=dfn-relative-high-resolution-coarse-time |
Other ids that conversions lose:
id="wall-clock-unsafe-current-time" for <dfn data-dfn-for="wall clock">unsafe current time</dfn>.<section id=...>, or generates one from the heading text. When you drop the wrapper, put the baseline's id on the heading, even if it doesn't match how other sections are named. hr-time lost #moments-and-durations by renaming it #sec-moments-and-durations. ReSpec's numbered heading ids (x2-1-...) don't need to be kept.#example-1, #example-2, in document order). Keep these ids (see "Examples" above).idl-def-* id (e.g. #idl-def-performance-now). Keep these with oldids on the prose <dfn> for that construct (<dfn data-export oldids=idl-def-performance-now>now()</dfn>), or as the id of a <pre class=idl> block that defines only that construct.<dfn data-cite="SPEC#frag"> has a local id (e.g. #dfn-date). Once it's a plain link, keep the id on the link: <a id=dfn-date href="...">Date</a>.#conformance is #w3c-conformance in Bikeshed. End the document with <div id=conformance></div>, which lands right before Bikeshed's Conformance section.To keep an old id working next to a new one, use oldids: <h3 id=new-id oldids="old-id,older-id">. It works on headings, <dfn>s, and examples. On other elements, such as <a> or <span>, it does nothing, so use id.
Exported terms. Other specs autolink to exported terms by type, for, and linking text, so keep all three.
data-lt replaces the element's text in Bikeshed, but adds to it in ReSpec. <dfn data-export data-lt="moment">coarsened moments</dfn> exports "moment" and "coarsened moments" in ReSpec, but only "moment" in Bikeshed, which broke cross-spec references to it (w3c/hr-time#175). Add the text to data-lt: data-lt="moment|coarsened moments".
When converting <dfn> elements inside a <dl> that describes IDL interface attributes, add the attribute keyword to each <dfn> so Bikeshed knows they define IDL attributes (not plain terms). The parent <dl> should have dfn-for set to the interface name.
<!-- BAD: Bikeshed treats these as plain concept dfns -->
<dl dfn-for="PerformanceEntry" data-export>
<dt><dfn>duration</dfn></dt>
<dd>...</dd>
</dl>
<!-- GOOD: Bikeshed knows these are IDL attributes -->
<dl dfn-for="PerformanceEntry" data-export>
<dt><dfn attribute>duration</dfn></dt>
<dd>...</dd>
</dl>Without the attribute keyword, Bikeshed won't correctly associate the dfn with the IDL attribute, and cross-references like {{PerformanceEntry/duration}} may fail to resolve.
.pr-preview.json:
{
"src_file": "index.bs",
"type": "bikeshed"
}.github/workflows/auto-publish.yml:
main (repos converted to Bikeshed typically use main as the source branch, with gh-pages reserved for published output)TOOLCHAIN: bikeshedGH_PAGES_BRANCH: gh-pages so spec-prod deploys the built spec to GitHub Pages (without this, no deployment happens)actions/checkout to v4W3C_BUILD_OVERRIDE to Bikeshed metadata format:W3C_BUILD_OVERRIDE: |
Shortname: the-shortname
Status: WDindex.html (replaced by index.bs)tidyconfig.txt if present (no longer needed).github/workflows/tidy.yml if present (no longer needed)Run bikeshed -f spec index.bs and fix ALL errors, warnings, and info messages iteratively until the build produces ZERO output.
Common issues and fixes:
| Issue | Fix |
|---|---|
|var:type| rendered literally in output | Critical: |var:type| is ReSpec-only; convert to plain |var| |
Single-use |var| warnings | Replace with <var ignore>var</var> |
| Obsolete RFC refs (e.g., RFC7234) | Use [[RFC7234 obsolete]] |
| Duplicate IDs | Add explicit id= to one element, or rename |
| Ambiguous dfn refs (origin, navigable, etc.) | Use explicit <a href="..."> or add to link-defaults/anchors |
Ambiguous local dfn with multiple for variants | Use explicit <a href="#anchor-id">text</a> |
[=Window/load=] not found | HTML spec doesn't export it; use explicit <a href> |
{{Interface.member}} not found | Use {{Interface/member}} (slash, not dot) |
Duplicate toJSON() dfns | Add data-dfn-for="InterfaceName" to parent <dl> |
| RFC2119 keywords in non-normative sections | Wrap with <span class=allow-2119> |
| Image size warning | Add width and height attributes |
| Unexported dfn warnings | Add data-export to <dfn> elements |
reload/navigate clashing with local enum values | Keep as explicit <a href> |
[[?REF]] not parsed | Use [[REF]] (Bikeshed uses [[!REF]] for normative, no ? prefix) |
Markdown parser conflicts with <li> containing <p>/<div> | Restructure as definition list or flatten; move notes outside list |
Multiple interfaces with same normalized ID (e.g. Performance vs performance) | Add explicit id= to one <dfn> |
| Unused local biblio entries | Remove unused entries or add [[REF]] citation in text |
[=Realm/global object=] not found | Use [=/global object=] instead |
| Multiple possible refs for SharedWorker, BroadcastChannel, etc. | Add to <pre class=link-defaults> |
bikeshed -f spec index.bs — must produce ZERO output (no errors, no warnings, no info messages). If there is ANY output, fix it before proceeding.check-anchors.py from this skill's directory:
bikeshed spec index.bs /tmp/<repo>-after.html
python3 <skill-dir>/check-anchors.py --before /tmp/<repo>-before.html --after /tmp/<repo>-after.htmlOK. For every id or exported term it reports missing, fix index.bs as described in "Keep anchors and exported terms stable", then rerun. Fix WARN lines too. Don't edit the script's ignore list to make it pass. It only skips ids that ReSpec regenerates on every build (ref-for-*, index-term-*, numbered heading ids, ...), which --show-ignored lists.|var:type| patterns do NOT appear literally in the rendered output — search the generated HTML for pipe characters followed by colonsindex.html (don't commit the build artifact)check-anchors.py passed against the ReSpec baseline.w3c/web-performance
40dea04
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.