meta
meta copied to clipboard
Formalizing domintro typography, etc.
It'd be nice to get some consistency in the "domintro" boxes for specs. By those I mean the green "For web developers (non-normative)" boxes like in:
- https://html.spec.whatwg.org/multipage/history.html#the-history-interface
- https://streams.spec.whatwg.org/#rs-prototype
- https://dom.spec.whatwg.org/#event-path-slot-in-closed-tree
- https://wicg.github.io/kv-storage/#KVStorageArea-constructor and other sections of that spec (mentioned as I wrote it pretty recently)
Easy-ish issues worth thinking about:
-
Typography. I prefer making everything inside the
<code>block, with nested<var>s inside for things that would be variable in JavaScript. HTML does not do this, but most other specs seem to, most of the time. -
Spacing around method/constructor-call parentheses. HTML seems to be pretty inconsistent. Most other specs have no spaces, like idiomatic JavaScript code.
-
Spacing around
.s. Everyone seems to add spaces, even though it's not idiomatic JavaScript. Probably worth keeping; it's a nice visual separator. -
Semicolon usage. I noticed one, but probably we shouldn't.
-
Promise-returning methods. I like using
awaitsyntax with these. -
Making sure we settle on conventions for things like constructors, (async) iterators, etc.
-
Getters. Do we do
value = interface . prop? Justinterface . prop?semanticName = interface . prop, like Stream'sisLocked = stream . locked? This seems very inconsistent across and within specs. -
Setters in combination with getters. We seem to do
interface . prop [ = value ]. An alternative would be two<dt>s, one for the setter and one for the getter. Or just two entirely separate<dt>/<dd>pairs. -
Varargs currently use the syntax
[arguments...](see below on the brackets). Should we align with JavaScript and use...argumentsor similar? See setTimeout/setInterval. -
Spacing around parameter lists, optional arguments (see below), etc.
Medium-difficulty issues worth thinking about:
-
Return values from methods. Undefined-returning methods can be written simply, but should methods that return something be written with their return values shown? Probably yes.
-
But is it OK to omit the leading
var/let/const? We have so far (mostly) -
What about in cases of destructuring, e.g.
{ database, store, version } = storage.backingStorefrom KV Storage, or[branch1, branch2] = stream.tee()from Streams? Those aren't valid JS statements; you'd need to either add the leadingvar/let/const, or wrap the whole expression in parentheses. Maybe that's fine and we just say domintros use expression syntax?
-
-
When do we prefix APIs with
window .orself .? And if we do, should we treat them as variables (i.e., documenting that these APIs work on anyWindowor anyWindowOrWorkerGlobalScope)? Or should we treat them as global properties, since that's the 99% use case? E.g.:-
HTML does
window . history . length, andwindow . document(note the<var>s aroundwindow, indicating that it can be used with anyWindowinstance. -
But it does
self . navigator . onLine, which is a bit strange; usingselfas a variable isn't something you'd expect. MaybewindowOrWorkerGlobalScope . navigator . onLine, but that's gross. -
Most
Navigatorproperties are prefixed withself . navigator ., but the Window-only ones are prefixed withwindow . navigator .. So although it might feel like the leading object is redundant, in this case it's valuable? -
Elsewhere HTML does
self . originwith no variable. -
Elsewhere it does
location . hash, which I guess is reasonable since multiplelocationobjects are accessible. But why no leadingwindow? -
No other specs seem to use the leading
window .orself ..
-
Harder issues worth thinking about:
-
Optional arguments. We use syntax like
interface . method(foo [ , bar ]). This is OK-ish, but for single optional arguments (like in Streams'sstream.cancel([ reason ]), it looks like we're passing an array. Can we do better?- Also I noticed that at least one place includes the default value.
-
"Objet literal syntax" for options arguments. This is probably best left to editor discretion, but perhaps we can come up with some guidelines. Compare the various methods Streams links above. Things to note:
-
In
new ReadableStream(underlingSource[, strategy]),underlyingSourceandstrategyare both dictionaries. However, they are first-class concepts, so I left them as variables instead of expanding them. -
I have two separate entries for
stream.getReader()andstream.getReader({ mode: "byob" }), since they behave pretty differently, despite the IDL beingoptional ReadableStreamGetReaderOptions options = {}. -
I did not add optionality brackets to any of the dictionary members in
stream.pipeTo(destination[, { preventClose, preventAbort, preventCancel, signal }]), despite them all being optional. It would have gotten pretty unwieldy. -
I did not add values for the dictionary members in
pipeTo(), even though for the booleans at least, it might have been reasonable. Contrast withstream.getReader({ mode: "byob" }), where I did.
-
/cc @whatwg/documentation
Yes to this issue. Not sure of the best way to make progress on each line item...
Agree with the "easy" ones where @domenic expresses an opinion.
Re: getters - isLocked = stream . locked seems redundant. Vote for "no variable"
Re: omit var/let/const - yes
Re: statements vs. expressions - well, we don't require semicolons, so +1 to "say domintros use expression syntax"
Re: When do we prefix APIs with window. or self.? - with my bias acknowledged, I'd say for window-scoped APIs, window. as a prefix is helpful, but for window-or-worker then I'd leave off self. which non-spec-wonks don't write (or so I've heard). I realize it's not a boolean conditions since there are more than two types of globals, tho.
Re: options dictionaries - https://wicg.github.io/web-locks/#api-lock-manager-request is another example which calls out the options in a more documentation-like way using a nested <dl>, rather than object literal syntax. In contrast, the transaction = connection . transaction(scope [, mode [, options ] ]) example just above https://w3c.github.io/IndexedDB/#dom-idbdatabase-transaction tries to explain the options in prose, which is nasty. A convention here would be great.
Re: optional args - yes, [ , bar ] is terrible and we should do better, no great suggestions though. What do JS libraries use as documentation conventions? Maybe just listing the permutations out as "overloads" is clearest?
Bikeshed could maybe help with formatting and linking if we follow a strict template?
cc @tabatkins
Easy-ish issues worth thinking about:
- Typography. I prefer making everything inside the
<code>block, with nested<var>s inside for things that would be variable in JavaScript. HTML does not do this, but most other specs seem to, most of the time.
Agreed. Ideally, as much as possible would be linked to their IDL types, such as the method name, the args, the return value.
- Spacing around method/constructor-call parentheses. HTML seems to be pretty inconsistent. Most other specs have no spaces, like idiomatic JavaScript code.
I'd be happy to follow some agreed set of https://prettier.io/playground/ rules.
- Spacing around
.s. Everyone seems to add spaces, even though it's not idiomatic JavaScript. Probably worth keeping; it's a nice visual separator.
I find it confusing to read fwiw.
- Semicolon usage. I noticed one, but probably we shouldn't.
I use them in code, but no strong feelings here. Again, happy with some Prettier rules.
- Promise-returning methods. I like using
awaitsyntax with these.
Agreed.
- Making sure we settle on conventions for things like constructors, (async) iterators, etc.
const thing = new Thing();
for await (const thing of iterable) { /* … */ }
- Getters. Do we do
value = interface . prop? Justinterface . prop?semanticName = interface . prop, like Stream'sisLocked = stream . locked? This seems very inconsistent across and within specs.
Don't think it needs a var.
- Setters in combination with getters. We seem to do
interface . prop [ = value ]. An alternative would be two<dt>s, one for the setter and one for the getter. Or just two entirely separate<dt>/<dd>pairs.
Two separate pairs I reckon.
- Varargs currently use the syntax
[arguments...](see below on the brackets). Should we align with JavaScript and use...argumentsor similar? See setTimeout/setInterval.
Agree with ...arguments.
- Spacing around parameter lists, optional arguments (see below), etc.
Happy with Prettier rules for spacing.
Medium-difficulty issues worth thinking about:
- Return values from methods. Undefined-returning methods can be written simply, but should methods that return something be written with their return values shown? Probably yes.
Yeah, and the resulting var should link to the IDL type.
- But is it OK to omit the leading
var/let/const? We have so far (mostly)
I'd prefer one of var/let/const, just so it looks like good practice.
- What about in cases of destructuring, e.g.
{ database, store, version } = storage.backingStorefrom KV Storage, or[branch1, branch2] = stream.tee()from Streams? Those aren't valid JS statements; you'd need to either add the leadingvar/let/const, or wrap the whole expression in parentheses. Maybe that's fine and we just say domintros use expression syntax?
Using destructuring if the return type is a dictionary seems ok. Again it'd be great if they link to their types. I agree it should have var/let/const.
When do we prefix APIs with
window .orself .? And if we do, should we treat them as variables (i.e., documenting that these APIs work on anyWindowor anyWindowOrWorkerGlobalScope)? Or should we treat them as global properties, since that's the 99% use case? E.g.:
- HTML does
window . history . length, andwindow . document(note the<var>s aroundwindow, indicating that it can be used with anyWindowinstance.- But it does
self . navigator . onLine, which is a bit strange; usingselfas a variable isn't something you'd expect. MaybewindowOrWorkerGlobalScope . navigator . onLine, but that's gross.- Most
Navigatorproperties are prefixed withself . navigator ., but the Window-only ones are prefixed withwindow . navigator .. So although it might feel like the leading object is redundant, in this case it's valuable?- Elsewhere HTML does
self . originwith no variable.- Elsewhere it does
location . hash, which I guess is reasonable since multiplelocationobjects are accessible. But why no leadingwindow?- No other specs seem to use the leading
window .orself ..
Fwiw, I suggested this set of rules for MDN but I don't think they did anything with it:
- If the item or its parent is available globally under a common name, use the name.
document.querySelector.skipWaitingin service worker.caches.matchin service worker.
- If the item is an instance method/property, use the constructor name with the first set of caps lower-cased.
element.getBoundingClientRect
- Otherwise, just use the name.
FetchEvent
If something is only available in a particular context, I wonder if we can come up with a visual 'tag' of sorts.
Harder issues worth thinking about:
Optional arguments. We use syntax like
interface . method(foo [ , bar ]). This is OK-ish, but for single optional arguments (like in Streams'sstream.cancel([ reason ]), it looks like we're passing an array. Can we do better?
- Also I noticed that at least one place includes the default value.
"Objet literal syntax" for options arguments. This is probably best left to editor discretion, but perhaps we can come up with some guidelines. Compare the various methods Streams links above. Things to note:
- In
new ReadableStream(underlingSource[, strategy]),underlyingSourceandstrategyare both dictionaries. However, they are first-class concepts, so I left them as variables instead of expanding them.- I have two separate entries for
stream.getReader()andstream.getReader({ mode: "byob" }), since they behave pretty differently, despite the IDL beingoptional ReadableStreamGetReaderOptions options = {}.- I did not add optionality brackets to any of the dictionary members in
stream.pipeTo(destination[, { preventClose, preventAbort, preventCancel, signal }]), despite them all being optional. It would have gotten pretty unwieldy.- I did not add values for the dictionary members in
pipeTo(), even though for the booleans at least, it might have been reasonable. Contrast withstream.getReader({ mode: "byob" }), where I did.
I like foo(bar?) to indicate optional, and foo(bar = 'whatever') for optional with default value.
I like 'destructuring' for option objects too:
const result = whatever({
foo = 'blah',
bar?
}?);
Here's a halfway point that tries to decide the easy stuff. I might do a pass on HTML to try to uniformize things and gather examples to help with the harder stuff.
Decisions
- Typography and code style:
- Everything inside
<code> - No spaces around parentheses
- No spaces around
.s - No semicolons
- Everything inside
- Getters/setters:
- Separate
<dt>/<dd>pairs for getters/setters. (This departs from most existing practice, but looking at a bunch of examples it will be an improvement, e.g. many sentences apply only to the setter or only to the getter. And it avoids the []-brackets.) - Do not have a getter return value variable
- Exception: getters which behave the same as methods use variable return values. E.g. you do
value = obj.item(x)+value = obj[x]instead ofvalue = obj.item(x)+obj[x].
- Exception: getters which behave the same as methods use variable return values. E.g. you do
- Default to value for the setter right-hand side but use a semantic name if you need to show multiple overloads or similar
- Separate
- Methods/constructors: no
var/let/const - Varargs: use leading
...like JavaScript - Promise-returning: use
await
Not yet decided
- Optional argument syntax
- What "base" to prefix things with
- Destructuring for return values
- Object literal syntax for options arguments
Canonical examples
Getter-only properties:
<dt><code><var>event</var>.<a href="...">bubbles</a></code>
<dd>Returns ... reference <var>event</var> ...
Getter/setter properties:
<dt><code><var>title</var>.<a href="...">text</a></code>
<dd>Returns ... reference <var>title</var> ...
<dt><code><var>title</var>.<a href="...">text</a> = <var>value</var></code>
<dd>... reference <var>title</var> and <var>value</var> ...
Undefined-returning methods:
<dt><code><var>table</var>.<a href="...">deleteCaption</a>()</code>
<dd>... reference <var>table</var> ...
Non-optional argument taking methods:
<dt><code><var>table</var>.<a href="...">deleteRow</a>(<var>index</var>)</code>
<dd>... reference <var>table</var> and <var>index</var> ...
Value-returning methods:
<dt><code><var>tfoot</var> = <var>table</var>.<a href="...">createTFoot</a>()</code>
<dd>... reference <var>table</var>, maybe reference <var>tfoot</var> ...
Varargs methods:
<dt><code><var>node</var>.<a href="...">append</a>(...<var>nodes</var>)</a></code>
<dd>... reference <var>node</var> and <var>nodes</var> ...</dd>
Constructors:
<dt><code><var>document</var> = new <a href="...">Document</a>()</code>
<dd>Constructs ...</dd>
Promise-for-undefined returning methods:
<dt><code>await <var>image</var>.<a href="...">decode</a>()</code>
<dd>Causes ..., returning a promise that fulfills when... The promise will be rejected when...</dd> <!-- or similar -->
Promise-for-value returning methods:
<dt><code><var>constructor</var> = await <var>customElements</var>.<a href="...">whenDefined</a>(<var>name</var>)</code>
<dd>Returns a promise that will be fulfilled with... The promise will be rejected if... reference <var>name</var>...</dd>
(note: customElements is not final and is subject to the outstanding "base" debate.)
Re: optional args
The MDN style guide on this lists out the permutations as multiple lines. There's a "Formal Syntax" section as well, but that would not be relevant for domintro. There was recent discussion leading to this.
Bikeshed could maybe help with formatting and linking if we follow a strict template?
Potentially, tho it looks like there's a pretty wide set of variations here that would make it difficult, with vars in different places and multi-level dot-access (so I can't reasonably infer the for value of later bits), plus things being set to other things...
Going thru Domenic's set of canonical examples and trying out some possible microsyntaxes, it's not gaining much, and doesn't meaningfully restrict what is put in there, either.
Tho, hm, perhaps a more expansive set of microsyntaxes (rather than trying to handle everything in one) could work...
Spaces around
.s
Like @jakearchibald, I find the spaces around . to be confusing to read. It doesn't look like standard JS; of your examples in the OP, I find the ones without spaces way more readable.
The best I can see for a markup shorthand is:
Getter-only properties:
<dt><code><var>event</var> . <a href="...">bubbles</a></code>
<dt><idl getter for=Event>event.bubbles</idl>
<dd>Returns ... reference <var>event</var> ...
Getter/setter properties:
<dt><code><var>title</var> . <a href="...">text</a></code>
<dt><idl getter for=HTMLTitleElement>title.text</idl>
<dd>Returns ... reference <var>title</var> ...
<dt><code><var>title</var> . <a href="...">text</a> = <var>value</var></code>
<dt><idl setter for=HTMLTitleElement>title.text = value</idl>
<dd>... reference <var>title</var> and <var>value</var> ...
Undefined-returning methods:
<dt><code><var>table</var> . <a href="...">deleteCaption</a>()</code>
<dt><idl method for=HTMLTableElement>table.deleteCaption()</idl>
<dd>... reference <var>table</var> ...
Non-optional argument taking methods:
<dt><code><var>table</var> . <a href="...">deleteRow</a>(<var>index</var>)</code>
<dt><idl method for=HTMLTableElement>table.deleteRow(index)</idl>
<dd>... reference <var>table</var> and <var>index</var> ...
Value-returning methods:
<dt><code><var>tfoot</var> = <var>table</var> . <a href="...">createTFoot</a>()</code>
<dt><idl method for=HTMLTableElement>tfoot = table.createTFoot()</idl>
<dd>... reference <var>table</var>, maybe reference <var>tfoot</var> ...
Varargs methods:
<dt><code><var>node</var> . <a href="...">append</a>(...<var>nodes</var>)</a></code>
<dt><idl method for=Node>node.append(...nodes)</idl>
<dd>... reference <var>node</var> and <var>nodes</var> ...</dd>
Constructors:
<dt><code><var>document</var> = new <a href="...">Document</a>()</code>
<dt><idl constructor>document = new Document()</idl>
<dd>Constructs ...</dd>
Promise-for-undefined returning methods:
<dt><code>await <var>image</var> . <a href="...">decode</a>()</code>
<dt><idl promise-method for=HTMLImageElement>await image.decode()</idl>
<dd>Causes ..., returning a promise that fulfills when... The promise will be rejected when...</dd> <!-- or similar -->
Promise-for-value returning methods:
<dt><code><var>constructor</var> = await <var>customElements</var> . <a href="...">whenDefined</a>(<var>name</var>)</code>
<dt><idl promise-method for=HTMLImageElement>constructor = await customElements.whenDefined(name)</idl>
<dd>Returns a promise that will be fulfilled with... The promise will be rejected if... reference <var>name</var>...</dd>
(both of these would throw a Bikeshed error if await didn't show up in the text)
Dunno if this is worthwhile.
If Bikeshed supports <dl domintro> (translated to <dl class=domintro>) it could also insert <code> into the <dt>s and perhaps inside <dt>s it could then change {{Document}}() to <a ...>Document</a>() and not do <code> insertion and such. That would reduce clutter a lot. Or even {{Class/method(arg, arg2)}} gets turned into <a ...>method</a>(<var>arg</var>, <var>arg2</var>).
Are there any examples of domintro blocks describing EventHandlers? (I didn't spot any in a quick check.) Should we have them?
Are there any examples of domintro blocks describing EventHandlers? (I didn't spot any in a quick check.) Should we have them?
We have the event-handler index at https://html.spec.whatwg.org/multipage/indices.html#ix-event-handlers
And there are a few others at places like https://html.spec.whatwg.org/multipage/media.html#cue-events in the body of the spec.
Whether documenting them that way is better or worse for developer usability than having domintro blocks for them, I dunno.