Skip to content
Marcos Caceres edited this page Mar 28, 2026 · 3 revisions

Writing Algorithms in ReSpec

Specifications define normative behavior through algorithms — numbered step sequences that unambiguously describe what a user agent must do. ReSpec provides markup support for algorithm authoring.

Basic algorithm markup

<section data-dfn-for="Widget">
  <h2>The <code>activate()</code> method</h2>
  <pre class="idl">
  interface Widget {
    undefined activate(optional WidgetOptions options = {});
  };
  </pre>

  <p>The <dfn>activate(options)</dfn> method steps are:</p>
  <ol class="algorithm">
    <li>If [=this=]'s [=Widget/active flag=] is true, return.</li>
    <li>Let |opts:WidgetOptions| be the result of [=converting=] |options|.</li>
    <li>Set [=this=]'s [=Widget/active flag=] to true.</li>
    <li>[=Fire an event=] named `activate` at [=this=].</li>
    <li>Return undefined.</li>
  </ol>
</section>

The class="algorithm" on <ol> enables:

  • Variable highlighting (when highlightVars is enabled)
  • Unused variable detection (when the no-unused-vars linting rule is enabled)
  • Assert step detection (steps starting "Assert:" get a special style)

Algorithm prose conventions

These conventions are used throughout W3C and WHATWG specifications. Following them makes your spec consistent with the broader ecosystem.

Variables

Use |variable:Type| for typed variables, or |variable| for untyped:

<ol class="algorithm">
  <li>Let |request:Request| be a new [=request=].</li>
  <li>Let |url:URL| be the result of [=URL parser|parsing=] |input|.</li>
  <li>Let |n| be 0.</li>
</ol>
  • Declare with "Let |x| be …"
  • Assign with "Set |x| to …"
  • Types are informational — use the IDL interface name or a descriptive type

Standard verbs

Use these consistently:

Verb Use for
Let x be y Declaring a new variable
Set x to y Reassigning an existing variable
Return x Returning a value (ends execution)
Throw x Throwing an exception (ends execution)
Assert: condition Invariant that must hold (starts with "Assert:")
If condition, then Conditional (inline or block)
For each x of list Iteration
Append x to list Adding to a list

[=this=] — referring to the current object

Use [=this=] to refer to the object the method is called on. This links to the Infra definition of "this":

<ol class="algorithm">
  <li>If [=this=]'s [=Widget/active flag=] is true, then throw an
      {{InvalidStateError}}.</li>
  <li>Set [=this=]'s [=Widget/name=] to |name|.</li>
  <li>Return [=this=].</li>
</ol>

Assert steps

Steps starting with "Assert:" are assertions — conditions that must be true at that point. ReSpec links "Assert" to the Infra definition automatically and styles them distinctively:

<ol class="algorithm">
  <li>Assert: |request|'s [=request/URL=] is not null.</li>
  <li>Assert: |options| [=map/contains=] "method".</li>
</ol>

Nested algorithms

Indent using nested <ol class="algorithm"> for sub-steps:

<ol class="algorithm">
  <li>For each |header| of |request|'s [=request/header list=]:
    <ol>
      <li>If |header|'s [=header/name=] is `authorization`, then return failure.</li>
    </ol>
  </li>
  <li>Return success.</li>
</ol>

Linking within algorithms

Use [=term=] and {{IDL}} shorthands as normal. For scoped members, use [=Interface/member=]:

<ol class="algorithm">
  <li>Let |url| be the result of [=URL parser|parsing=] |input| with |base|.</li>
  <li>If |url| is failure, then [=throw=] a {{TypeError}}.</li>
  <li>Set [=this=]'s [=Request/url=] to |url|.</li>
</ol>

Abstract operations

Define reusable sub-algorithms as abstract operations:

<section>
  <h3>To <dfn data-dfn-type="abstract-op">validate a widget</dfn>, given a |widget:Widget|:</h3>
  <ol class="algorithm">
    <li>If |widget|'s [=Widget/name=] is the empty string, return false.</li>
    <li>Return true.</li>
  </ol>
</section>

Reference abstract operations with [=validate a widget=]:

<ol class="algorithm">
  <li>If [=validate a widget=] with |w| returns false, throw a {{TypeError}}.</li>
</ol>

Common patterns

Promise-returning algorithms

Methods that return a {{Promise}} follow a standard structure. The primitives are defined in WEBIDL:

<p>The <dfn>load(url)</dfn> method steps are:</p>
<ol class="algorithm">
  <li>Let |p:Promise| be [=a new promise=].</li>
  <li>If |url| is not [=valid=], then [=reject=] |p| with a {{TypeError}}
      and return |p|.</li>
  <li>Run [=fetch the resource=] with |url| and |p|.</li>
  <li>Return |p|.</li>
</ol>

Key promise primitives from WEBIDL — enable with xref: ["WEBIDL"] or xref: "web-platform":

Phrase Meaning
[=a new promise=] Create a new pending Promise
[=a promise resolved with=] |value| Create an already-resolved Promise
[=a promise rejected with=] |reason| Create an already-rejected Promise
[=resolve=] |p| with |value| Settle an existing Promise as resolved
[=reject=] |p| with |reason| Settle an existing Promise as rejected
[=upon fulfillment=] of |p| Run steps when promise resolves
[=upon rejection=] of |p| Run steps when promise rejects
[=promise/react=] to |p| Run steps on fulfillment and/or rejection
[=wait for all=] with |promises| Wait for a list of promises

[=a promise resolved with=] vs [=resolve=]:

  • [=a promise resolved with=] x — creates and returns a new already-resolved promise. Use when returning immediately: return [=a promise resolved with=] undefined.
  • [=resolve=] |p| with x — settles an existing pending promise. Use with [=In parallel=] after doing work asynchronously.

Parallel algorithms

For operations that run asynchronously (most network and I/O operations), use [=In parallel=] from HTML:

<p>The <dfn>fetch(request)</dfn> method steps are:</p>
<ol class="algorithm">
  <li>Let |p:Promise| be [=a new promise=].</li>
  <li>[=In parallel=]:
    <ol>
      <li>Let |response| be the result of [=fetching=] |request|.</li>
      <li>If |response| is a failure, then [=queue a global task=] on
          the [=networking task source=] with [=this=]'s [=relevant global object=]
          to [=reject=] |p| with a {{TypeError}}.</li>
      <li>Otherwise, [=queue a global task=] to [=resolve=] |p|
          with |response|.</li>
    </ol>
  </li>
  <li>Return |p|.</li>
</ol>

Key HTML primitives — available with xref: "web-platform":

Phrase Meaning
[=In parallel=] Run the following steps concurrently
[=Queue a task=] Schedule work on the event loop
[=Queue a global task=] Schedule work on a specific global's event loop
[=Fire an event=] Dispatch a DOM event

Dictionary / options object access

WebIDL dictionary members are accessed using Infra map operations — enable with xref: ["INFRA"] or xref: "web-platform":

<ol class="algorithm">
  <li>If |options|["signal"] [=map/exists=], then:
    <ol>
      <li>If |options|["signal"] is [=AbortSignal/aborted=], return
          [=a promise rejected with=] |options|["signal"]'s
          [=AbortSignal/abort reason=].</li>
    </ol>
  </li>
  <li>Let |timeout| be |options|["timeout"] if it [=map/exists=],
      otherwise 5000.</li>
</ol>

Key Infra map operations — available with xref: "web-platform":

Operation Linking text Meaning
` map ["key"]`
[=map/exist=] or [=map/contain=] exist | contain Check if a key is present
[=map/set=] set Set a value at key
[=map/remove=] remove Remove an entry
[=map/for each=] for each Iterate over entries
[=map/size=] size Number of entries
[=map/is empty=] is empty True if no entries

Key Infra list operations:

Operation Linking text Meaning
[=list/append=] append Add item to end
[=list/contain=] contain Check membership
[=list/remove=] remove Remove item(s)
[=list/for each=] for each Iterate
[=list/size=] size Number of items
[=list/is empty=] is empty True if no items

Abort handling

For operations that support cancellation via {{AbortSignal}}:

<ol class="algorithm">
  <li>If |options|["signal"] [=map/exists=]:
    <ol>
      <li>If |options|["signal"] is [=AbortSignal/aborted=], return
          [=a promise rejected with=] |options|["signal"]'s
          [=AbortSignal/abort reason=].</li>
      <li>[=AbortSignal/add=] the following abort steps to
          |options|["signal"]:
        <ol>
          <li>[=Reject=] |p| with |options|["signal"]'s
              [=AbortSignal/abort reason=].</li>
        </ol>
      </li>
    </ol>
  </li>
</ol>

Enum values in prose

When defining prose descriptions for IDL enum values, use data-dfn-type="enum-value" and data-dfn-for:

<p>The <dfn data-dfn-type="enum-value" data-dfn-for="WidgetState">"idle"</dfn>
state means the widget has not yet been activated.</p>

<p>The <dfn data-dfn-type="enum-value" data-dfn-for="WidgetState">"active"</dfn>
state means the widget is currently active.</p>

These can then be linked as {{WidgetState/"idle"}} in algorithm steps.


The data-algorithm attribute

Mark an algorithm with a name using data-algorithm:

<ol class="algorithm" data-algorithm="fetch">
  <li>...</li>
</ol>

This is useful for cross-referencing specific algorithms by name.


Tips

  • One action per step — don't combine unrelated operations in a single step
  • Terminate explicitly — every path should end with "return", "throw", or similar
  • Use Infra for primitives — it defines lists, maps, strings, and common operations used across W3C/WHATWG specs. Enable with xref: ["INFRA"].
  • Use WebIDL for promises — all promise creation and settling language comes from WebIDL. Enable with xref: ["WEBIDL"].
  • Link everything — terms, types, and operations should link to their definitions
  • Keep it deterministic — algorithms must have one unambiguous outcome for every input

Guides

Configuration options

W3C Configuration options

Linting rules

Internal properties

Handled by ReSpec for you.

Special <section> IDs

HTML elements

Custom Elements

HTML attributes

CSS Classes

Special properties

Clone this wiki locally