Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Markdown] Decide what to do about <dl> #4367

Closed
wbamberg opened this issue Apr 22, 2021 · 30 comments
Closed

[Markdown] Decide what to do about <dl> #4367

wbamberg opened this issue Apr 22, 2021 · 30 comments
Labels
Content:JS JavaScript docs

Comments

@wbamberg
Copy link
Collaborator

wbamberg commented Apr 22, 2021

In MDN we use <dl> (description lists aka definition lists) quite extensively, and they're not natively supported in GFM.

A quick look at the JS docs tells us what we already know: in 922 pages:

  • 518 of them use a description list at least once
  • 800 description lists are used in total

They're most commonly used for:

  • listing parameters of functions
  • listing exceptions thrown by functions
  • listing properties and methods of objects

Some options:

Generate them

If we could generate these lists (in a macro, or directly in Yari) we could of course emit HTML, and could then use <dl>.

We could possibly generate the lists of properties and methods in the future, because they are lists of links to separate pages. But it's difficult to see how we'd do the same for lists of parameters or exceptions. So this definitely isn't a solution now and probably isn't a complete solution ever.

Stop using them

There are so many of these that converting them would be hard, and it's hard to argue that they are not an appropriate choice for things like lists of parameters.

Use raw HTML

Unlike something like a note, where you just need opening and closing HTML tags and can have Markdown inside, using raw HTML for <dl> commits you of course to raw HTML for <dt> and <dd> as well. So you will end up with a lot of raw HTML here. It's not an attractive option.

Extend GFM

This leads us to the extend-GFM option. The most popular choice here is the one used in Kramdown:

First Term
: This is the definition of the first term.
: This is a second definition of the first term.

Second Term
: This is the definition of the second term.
<dl>
  <dt>First Term</dt>
  <dd>This is the definition of the first term.</dd>
  <dd>This a second definition of the first term.</dd>
  <dt>Second Term</dt>
  <dd>This is the definition of the second term. </dd>
</dl>

Kramdown describes this syntax in some detail: https://kramdown.gettalong.org/syntax.html#definition-lists and it's not very clear to me how much we would want to adopt this definition wholesale or how much we would want or need to constrain it. Not to mention what it would be like to implement: it would be really interesting to hear what @Gregoor thinks about that. I don't know if there is already a Unified handler to support this syntax.

(One obvious point is that we wouldn't support inline attribute lists (IALs, https://kramdown.gettalong.org/syntax.html#inline-attribute-lists.)

@wbamberg wbamberg added the needs triage Triage needed by staff and/or partners. Automatically applied when an issue is opened. label Apr 22, 2021
@wbamberg wbamberg added this to To do in MDN to Markdown Apr 22, 2021
@chrisdavidmills
Copy link
Contributor

My vote is for the "Extend GFM" option. My main concerns with this are:

  1. How well it displays on GitHub. I'm expecting it to be OK; the colon at start of sentence is quite distinctive.
  2. How well it works when nesting other content inside of dd/dt. If this is an issue, would it be an option to see how many complex dls we have, and look to convert them to something else, or just constain usage of this syntax to simple dls and do something else for complex exceptions.
@Gregoor
Copy link
Contributor

Gregoor commented Apr 22, 2021

Here is another idea, how about we re-use markdown tables for definition lists? We could turn 2-columned tables with headers like in the example into definition lists.

Example:

$dt $dd
variable On each iteration a value of a different property is assigned to variable. variable may be declared with const, let, or var.
iterable Object whose iterable properties are iterated.

Upside:

  • no new syntax
  • renders nicely on GitHub

Downside:

  • overloads table syntax
@Ryuno-Ki
Copy link
Collaborator

Extending GFM feels a bit like going towards a MediaWiki-esque syntax in my opinion.

Nesting is an interesting point. Even if we went into the table variant @Gregoor described, that wouldn't save us completely (think of nested tables).

Ideally, I'd like to see some generated.
But that requires either a macro (we want to get away from them) to demark the position where the <dl> shall be injected - or a well-defined position within the page. How would you define that? I could imagine a HTML comment here …

@hamishwillee
Copy link
Collaborator

I'm with extending GFM. For your edification, below is HTML, Kramdown and table version taken from a page in the docs.

  • The Kramdown syntax is pretty well understood and works OK in github. It also can support nested markdown so you can convert everything.
  • I really like Gregoor's suggestion too. It would work for almost every table. However it is not quite as flexible because:
    • you can't easily nest things like bulleted lists inside a markdown table. Below I just removed the list.
    • Having a second definition for a term doesn't look great (I added a case below to demo). You could render this in other ways of course, but hard to automate the conversion.
Raw HTML version
Cascade and inheritance
The aim of this lesson is to develop your understanding of some of the most fundamental concepts of CSS — the cascade, specificity, and inheritance — which control how CSS is applied to HTML and how conflicts are resolved.
CSS selectors
There are a wide variety of CSS selectors available, allowing for fine-grained precision when selecting elements to style. In this article and its sub-articles, we'll run through the different types in great detail, seeing how they work. The sub-articles are as follows:
The box model
Everything in CSS has a box around it, and understanding these boxes is key to being able to create layouts with CSS, or to align items with other items. In this lesson, we will take a proper look at the CSS Box Model, in order that you can move onto more complex layout tasks with an understanding of how it works and the terminology that relates to it.
Backgrounds and borders
In this lesson we will take a look at some of the creative things you can do with CSS backgrounds and borders. From adding gradients, background images, and rounded corners, backgrounds and borders are the answer to a lot of styling questions in CSS.
Handling different text directions
In recent years, CSS has evolved in order to better support different directionality of content, including right-to-left but also top-to-bottom content (such as Japanese) — these different directionalities are called writing modes. As you progress in your study and begin to work with layout, an understanding of writing modes will be very helpful to you, therefore we will introduce them in this article.
Overflowing content
In this lesson we will look at another important concept in CSS — overflow. Overflow is what happens when there is too much content to be contained comfortably inside a box. In this guide, you will learn what it is and how to manage it.
CSS values and units
Every property used in CSS has a value or set of values that are allowed for that property. In this lesson, we will take a look at some of the most common values and units in use.
Sizing items in CSS
In the various lessons so far you have come across a number of ways to size items on a web page using CSS. Understanding how big the different features in your design will be is important, and in this lesson, we will summarize the various ways elements get a size via CSS and define a few terms around sizing that will help you in the future.
Images, media, and form elements
In this lesson we will take a look at how certain special elements are treated in CSS. Images, other media, and form elements behave a little differently in terms of your ability to style them with CSS than regular boxes. Understanding what is and isn't possible can save some frustration, and this lesson will highlight some of the main things that you need to know.
Styling tables
Styling an HTML table isn't the most glamorous job in the world, but sometimes we all have to do it. This article provides a guide to making HTML tables look good, with some specific table styling techniques highlighted.
Debugging CSS
Sometimes when writing CSS you will encounter an issue where your CSS doesn't seem to be doing what you expect. This article will give you guidance on how to go about debugging a CSS problem, and show you how the DevTools included in all modern browsers can help you find out what is going on.
Organizing your CSS
As you start to work on larger stylesheets and big projects you will discover that maintaining a huge CSS file can be challenging. In this article, we will take a brief look at some best practices for writing your CSS to make it easily maintainable, and some of the solutions you will find in use by others to help improve maintainability.
Kramdown version

Cascade and inheritance
:The aim of this lesson is to develop your understanding of some of the most fundamental concepts of CSS — the cascade, specificity, and inheritance — which control how CSS is applied to HTML and how conflicts are resolved.

CSS selectors
:There are a wide variety of CSS selectors available, allowing for fine-grained precision when selecting elements to style. In this article and its sub-articles, we'll run through the different types in great detail, seeing how they work. The sub-articles are as follows:

The box model
:Everything in CSS has a box around it, and understanding these boxes is key to being able to create layouts with CSS, or to align items with other items. In this lesson, we will take a proper look at the CSS Box Model, in order that you can move onto more complex layout tasks with an understanding of how it works and the terminology that relates to it.

Backgrounds and borders
:In this lesson we will take a look at some of the creative things you can do with CSS backgrounds and borders. From adding gradients, background images, and rounded corners, backgrounds and borders are the answer to a lot of styling questions in CSS.

Handling different text directions
:In recent years, CSS has evolved in order to better support different directionality of content, including right-to-left but also top-to-bottom content (such as Japanese) — these different directionalities are called writing modes. As you progress in your study and begin to work with layout, an understanding of writing modes will be very helpful to you, therefore we will introduce them in this article.

Overflowing content
:In this lesson we will look at another important concept in CSS — overflow. Overflow is what happens when there is too much content to be contained comfortably inside a box. In this guide, you will learn what it is and how to manage it.

CSS values and units
:Every property used in CSS has a value or set of values that are allowed for that property. In this lesson, we will take a look at some of the most common values and units in use.

Sizing items in CSS
:In the various lessons so far you have come across a number of ways to size items on a web page using CSS. Understanding how big the different features in your design will be is important, and in this lesson, we will summarize the various ways elements get a size via CSS and define a few terms around sizing that will help you in the future.

Images, media, and form elements
:In this lesson we will take a look at how certain special elements are treated in CSS. Images, other media, and form elements behave a little differently in terms of your ability to style them with CSS than regular boxes. Understanding what is and isn't possible can save some frustration, and this lesson will highlight some of the main things that you need to know.

Styling tables
:Styling an HTML table isn't the most glamorous job in the world, but sometimes we all have to do it. This article provides a guide to making HTML tables look good, with some specific table styling techniques highlighted.

Debugging CSS
:Sometimes when writing CSS you will encounter an issue where your CSS doesn't seem to be doing what you expect. This article will give you guidance on how to go about debugging a CSS problem, and show you how the DevTools included in all modern browsers can help you find out what is going on.

Organizing your CSS
:As you start to work on larger stylesheets and big projects you will discover that maintaining a huge CSS file can be challenging. In this article, we will take a brief look at some best practices for writing your CSS to make it easily maintainable, and some of the solutions you will find in use by others to help improve maintainability.

Table version
dt dl
Cascade and inheritance The aim of this lesson is to develop your understanding of some of the most fundamental concepts of CSS — the cascade, specificity, and inheritance — which control how CSS is applied to HTML and how conflicts are resolved.
CSS selectors There are a wide variety of CSS selectors available, allowing for fine-grained precision when selecting elements to style. In this article and its sub-articles, we'll run through the different types in great detail, seeing how they work. The sub-articles are as follows: Type, class, and ID selectors, Attribute selectors, Pseudo-classes and pseudo-elements, Combinators
The box model Everything in CSS has a box around it, and understanding these boxes is key to being able to create layouts with CSS, or to align items with other items. In this lesson, we will take a proper look at the CSS Box Model, in order that you can move onto more complex layout tasks with an understanding of how it works and the terminology that relates to it.
  A second term, just to show a demo.
Backgrounds and borders In this lesson we will take a look at some of the creative things you can do with CSS backgrounds and borders. From adding gradients, background images, and rounded corners, backgrounds and borders are the answer to a lot of styling questions in CSS.
Handling different text directions In recent years, CSS has evolved in order to better support different directionality of content, including right-to-left but also top-to-bottom content (such as Japanese) — these different directionalities are called writing modes. As you progress in your study and begin to work with layout, an understanding of writing modes will be very helpful to you, therefore we will introduce them in this article.
Overflowing content In this lesson we will look at another important concept in CSS — overflow. Overflow is what happens when there is too much content to be contained comfortably inside a box. In this guide, you will learn what it is and how to manage it.
CSS values and units Every property used in CSS has a value or set of values that are allowed for that property. In this lesson, we will take a look at some of the most common values and units in use.
Sizing items in CSS In the various lessons so far you have come across a number of ways to size items on a web page using CSS. Understanding how big the different features in your design will be is important, and in this lesson, we will summarize the various ways elements get a size via CSS and define a few terms around sizing that will help you in the future.
Images, media, and form elements In this lesson we will take a look at how certain special elements are treated in CSS. Images, other media, and form elements behave a little differently in terms of your ability to style them with CSS than regular boxes. Understanding what is and isn't possible can save some frustration, and this lesson will highlight some of the main things that you need to know.
Styling tables Styling an HTML table isn't the most glamorous job in the world, but sometimes we all have to do it. This article provides a guide to making HTML tables look good, with some specific table styling techniques highlighted.
Debugging CSS Sometimes when writing CSS you will encounter an issue where your CSS doesn't seem to be doing what you expect. This article will give you guidance on how to go about debugging a CSS problem, and show you how the DevTools included in all modern browsers can help you find out what is going on.
Organizing your CSS As you start to work on larger stylesheets and big projects you will discover that maintaining a huge CSS file can be challenging. In this article, we will take a brief look at some best practices for writing your CSS to make it easily maintainable, and some of the solutions you will find in use by others to help improve maintainability.
  • Will, FYI I updated the description above to include possible second definition for a term
@wbamberg
Copy link
Collaborator Author

Thanks for all the thoughts, and thanks Hamish for showing the different syntaxes!

Here is another idea, how about we re-use markdown tables for definition lists? We could turn 2-columned tables with headers like in the example into definition lists.

I think this would be OK too. I think it's not strictly true to say "no new syntax" though, there is a new syntax, just one based on tables. I think as Hamish says, the Kramdown option seems quite commonly understood, and yes, it looks all right in the source.

About nesting: it is a thing. I count 57 pages in the JS docs where <dl> or <ul> is nested under a <dl>. Usually this is for things like callback arguments that take parameters, or listing the possible values that a parameter can have.

https://developer.mozilla.org/en-us/web/javascript/guide/regular_expressions/cheatsheet
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/every
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/filter
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/find
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/findindex
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/flatmap
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/foreach
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/map
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/reduce
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/reduceright
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/some
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/array/sort
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/date/date
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/generator/throw
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/collator/collator
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/collator/collator
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/collator/supportedlocalesof
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/datetimeformat/datetimeformat
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/datetimeformat/datetimeformat
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/datetimeformat/datetimeformat
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/datetimeformat/supportedlocalesof
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/displaynames/displaynames
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/displaynames/displaynames
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/displaynames/of
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/displaynames/supportedlocalesof
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/listformat/listformat
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/listformat/supportedlocalesof
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/numberformat/numberformat
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/numberformat/numberformat
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/numberformat/supportedlocalesof
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/pluralrules/pluralrules
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/pluralrules/pluralrules
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/pluralrules/supportedlocalesof
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/relativetimeformat/relativetimeformat
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/relativetimeformat/relativetimeformat
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/relativetimeformat/resolvedoptions
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/intl/relativetimeformat/supportedlocalesof
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/map/foreach
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/object/__definesetter__
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/object/defineproperties
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/promise/catch
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/regexp/compile
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/regexp/regexp
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/set/foreach
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/string/normalize
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/string/split
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/typedarray/every
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/typedarray/find
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/typedarray/findindex
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/typedarray/foreach
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/typedarray/map
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/typedarray/reduce
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/typedarray/reduceright
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/typedarray/some
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/webassembly/global/global
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/webassembly/memory/memory
https://developer.mozilla.org/en-us/web/javascript/reference/global_objects/webassembly/table/table

Ideally, I'd like to see some generated.
But that requires either a macro (we want to get away from them) to demark the position where the <dl> shall be injected - or a well-defined position within the page. How would you define that? I could imagine a HTML comment here …

Well... we could generate some of them, with a bit of work. And yes, we kinda want to get away from macros, eventually, but you need some code to generate things, and it has to live somewhere, and KS macros are the current somewhere.

For lists of pages, like, say, the static methods of Array, you could have (let's call it) a "thing" embedded in the page, that says "insert a list of the static methods right here", and when Yari builds the page it resolves that by finding the list of static methods (perhaps by convention, by finding the subpages with a "js-static-method" page type). Whether this work is done in a KS macro or in Yari platform code, that general solution is the same.

(Or, you could have something more structured, where a JS class page always gets a front matter item pointing at the pages describing its static methods, and Yari just knows that when it sees this it has to insert the list of pages at the defined location for that piece of content.)

But how would you do either of those things for, say, lists of parameters? You'd have to make authors semantically mark up that content somehow, so your builder can recognise it, and this makes writing an exercise in form-filling, which really goes against the idea of Markdown as a way of making authoring easier. So I think although we could (and eventually should) generate lots of <dl> elements, (1) that's in a further future and (2) we will probably still even then have a need for hand-written <dl> elements.

@hamishwillee
Copy link
Collaborator

About nesting: it is a thing. I count 57 pages in the JS docs where <dl> or <ul> is nested under a <dl>. Usually this is for things like callback arguments that take parameters, or listing the possible values that a parameter can have.

I was careless when I said this would all just work - needs a bit more thought for this case. While you can express a nested list kind of unambiguously you can't do so with this definition list syntax. That's because the first line has no marker. To markdown "ordinary rules" the new definition list looks like line continuations. Here is an example showing it failing:

locales
:A string with a BCP 47 language tag, or an array of such strings. For the general form of the locales ...
options {{optional_inline}}
:An object that may have the following property:
localeMatcher
:The locale matching algorithm to use. Possible values are "lookup" and "best fit"; the default is "best fit". For information about this option, see the...

I'm not sure how you would resolve this case in a way that works with github :-(

@ddbeck
Copy link
Contributor

ddbeck commented Apr 27, 2021

I don't know if there is already a Unified handler to support this syntax.

I know there's at least one partial implementation of the definition list: https://github.com/Symbitic/remark-plugins/tree/master/packages/remark-deflist. It has a limitation of only supporting single-paragraph definitions. Looking at the implementation, I'd guess that generalizing it to any sort of block is rather more complex. I'm not quite sure if can be done without actually implementing a new syntax at the micromark level.

Overloading lists

I have another proposal, inspired by the table suggestion and reStructuredText's list tables: how about using a magic unordered list?

We could have something like this:

source

* deflist
* term 1
  * definition 1
  * definition 2
* term 2
  * etc.

rendered (GFM)

  • deflist
  • term 1
    • definition 1
    • definition 2
  • term 2
    • etc.

rendered (Yari)

<dl>
  <dt>term 1</dt>
  <dd>definition 1</dd>
  <dd>definition 2</dd>
  <dt>term 2</dt>
  <dd>etc.</dd>
</dl>
term 1
definition 1
definition 2
term 2
etc.

There's other variations you could do (e.g., using a magic word at the start of each <li>, put deflist outside the list, use an ordered list, etc.), but this is the overall idea.

It has the same upsides as the table: no new syntax and renders nicely in GitHub. It also adds a couple of benefits on top: it doesn't use the unpleasant table syntax and accommodates block-level content inside definitions (raised by @wbamberg). It has a new downside of relying on a magic word, but we have already done that for notes and warnings.

As an implementation, transforming the underlying <ul> tree wouldn't be terribly complex, so you could implement it without directly parsing the source. (Some of the variations aren't so easy to implement, but most are.)

@hamishwillee
Copy link
Collaborator

What I like about @ddbeck suggestion of overloading lists #4367 (comment) is that it allows nested deflists, which is something that we do have in our sources. If we went this approach would be good to have a more visible keyword/list marker.

What I like about using the extended GFM is that it is clearly NOT an ordinary list. But that only supports single level. Can we live with that? Well, we can if we explicitly state that for the small number of nested definition list cases you can use HTML.

Upshot, IMO the overloaded lists is the best option I've seen so far.

@wbamberg
Copy link
Collaborator Author

What I like about using the extended GFM is that it is clearly NOT an ordinary list. But that only supports single level. Can we live with that? Well, we can if we explicitly state that for the small number of nested definition list cases you can use HTML.

By "extended GFM" do you mean what Kramdown does or something else? Because Kramdown does allow nesting (although the Unified implementation Daniel linked to above does not).

I'm not happy about it but I do think it will be difficult to live without nesting in our current docs.

@hamishwillee
Copy link
Collaborator

I meant Kramdown. How does that do nesting?

@wbamberg
Copy link
Collaborator Author

According to https://kramdown-sandbox.herokuapp.com/ , this markup:


One
: a thing

    A
    : another thing
    
    B
    : a different thing

Two
: back to the outer list

...gives us this HTML:

<dl>
  <dt>One</dt>
  <dd>a thing
    <dl>
      <dt>A</dt>
      <dd>another thing</dd>
      <dt>B</dt>
      <dd>a different thing</dd>
    </dl>
  </dd>
  <dt>Two</dt>
  <dd>back to the outer list</dd>
</dl>
@wbamberg
Copy link
Collaborator Author

wbamberg commented Apr 29, 2021

For authoring, I think I like the Kramdown syntax better, but @ddbeck 's would be OK too. And I agree that we should consider how hard it is to implement extensions.

I was going to say that as far as I know we almost never use multiple definitions for a single term, so it would probably be fine not to support this. But then I looked.... and we do things like this:

<dl>
<dt><code><var>start</var></code> {{optional_inline}}</dt>
<dd>Zero-based index at which to start extraction.</dd>
<dd>A negative index can be used, indicating an offset from the end of the sequence.
<code>slice(-2)</code> extracts the last two elements in the sequence.</dd>
<dd>If <code><var>start</var></code> is undefined, <code>slice</code> starts from the
index <code>0</code>.</dd>
<dd>If <code><var>start</var></code> is greater than the index range of the sequence, an
empty array is returned.</dd>
<dt><code><var>end</var></code> {{optional_inline}}</dt>
<dd>Zero-based index <em>before</em> which to end extraction. <code>slice</code>
extracts up to but not including <code><var>end</var></code>. For example,
<code>slice(1,4)</code> extracts the second element through the fourth element
(elements indexed 1, 2, and 3).</dd>
<dd>A negative index can be used, indicating an offset from the end of the sequence.
<code>slice(2,-1)</code> extracts the third element through the second-to-last element
in the sequence.</dd>
<dd>If <code><var>end</var></code> is omitted, <code>slice</code> extracts through the
end of the sequence (<code><var>arr</var>.length</code>).</dd>
<dd>If <code><var>end</var></code> is greater than the length of the sequence,
<code>slice</code> extracts through to the end of the sequence
(<code><var>arr</var>.length</code>).</dd>
</dl>

...which is obviously broken. There aren't too many of these (in JS at least), and I'd be open to trying to fix this markup. Unfortunately it's hard to automate since sometimes there might be actual real multiple definitions. I think hardly ever though.

@hamishwillee
Copy link
Collaborator

Thanks @wbamberg

Yes, we use nested lists a bit, mainly for options of arguments. I have a slight leaning towards Kramdown syntax too (for the same reason). As shown below, neither is awesome in github.


One
: a thing

A
: another thing

B
: a different thing

Two
: back to the outer list


  • deflist
  • One
    • deflist
    • A
      • another thing
    • B
      • a differentthing
  • Two
    • back to the outer list
@sideshowbarker
Copy link
Collaborator

sideshowbarker commented May 1, 2021

I really like @ddbeck’s suggestion in #4367 (comment) best. I think that’s the easiest thing for authors/contributors — it doesn’t require them to learn a new syntax. That along with the other advantages it has (ability to nest, ability to contain block content) seems to make it a pretty neat solution.

Also, I have a lot of experience creating and wrestling with normal lists in markdown — and I imagine we all do, and most contributors probably would as well. And that experience includes knowing already about some of the quirks it has — e.g., how to do nesting correctly, and how to include block content.

I think the "Extend GFM" option could possibly also be a viable solution, but I can’t speak from experience, because I’ve never used it anywhere yet — in contrast to having a lot of experience with using the normal list syntax in @ddbeck’s suggestion.

So it seems like the "Extend GFM" option is more of an unknown than the just-use-normal-lists-with-magic-keyword solution that @ddbeck proposed — that is, the "Extend GFM" option is more of risk to buy into than solution @ddbeck proposes.

I’d also be happy with the table-based way that @Gregoor suggested in #4367 (comment)if it handled block content. But from what others have said in this issue and elsewhere, it sounds like it doesn’t handle block content. If so, that’d kinda make it seem to be a non-starter.

I most dislike the "Use raw HTML" option, by a wide margin. Adopting that and telling authors/contributors they need to use it would seem to me to sort of being giving up on the fundamentals of why we’re making this move to begin with. If we’re going to help authors move away from being required to author in HTML to contribute to MDN, let’s really move away — all the way.

@hamishwillee
Copy link
Collaborator

I can live with either, and I keep changing my mind about which I prefer - right now it's the @ddbeck solution because I value github rendering. But if we did that I would prefer a much more obvious deflist identifier string.

I think there is clear leaning towards the two discussed above ^^^. Are we at the point of a vote?

@ddbeck
Copy link
Contributor

ddbeck commented May 4, 2021

Something to think about for any choice here: I suspect that, longer-term, there will be a lot of interest in giving structure to the things that we typically use definition lists for. For instance, it might be really nice to have separate files for object properties, with a more structured format (that we can check against IDL and the like), that can be used as source data for generating definition lists. It's likely that whatever we choose here is going to end up transitional (or niche use).

@sideshowbarker
Copy link
Collaborator

it might be really nice to have separate files for object properties, with a more structured format (that we can check against IDL and the like), that can be used as source data for generating definition lists

So that would be something like what we do with https://github.com/mdn/data for CSS syntax now, right?

I can imagine that as the basis for object properties, we could maintain copies of the IDL blocks, and use an IDL parser and some code to generate the documentation stubs that we then populate.

I think that unlike the CSS syntax case, we would need something more complicated like that in this case, because for object properties, we also have a lot of unstructured free-form prose that accompanies the structured (from IDL) information.

But all that said, once we get to that point, it seems like we’d still have the same problem as we do now, as far as needing to decide on some list syntax for authors to use. I mean, the authors wouldn’t need to touch the structured (IDL) source — but they’d still need to author the part that provides the free-form prose descriptions, associated with (mapped to) each IDL member.

The only alternatives I could imagine for managing the free-form prose in a way that associated it with the structured parts would be to add comments to the IDL, and extract those comments in the build — or else to manage the whole thing as JSON instead. But those clearly have pretty big disadvantages over having the source for the prose parts be in markdown.

@ddbeck
Copy link
Contributor

ddbeck commented May 5, 2021

So that would be something like what we do with https://github.com/mdn/data for CSS syntax now, right?

@sideshowbarker I don't want to get too far off-topic here, but in this case, I'm talking about revisiting some of the ideas that came out of Stumptown. For content about things which are themselves highly structured, I think there's the possibility of doing stuff like tightly controlling the form and sequence of Markdown headings to describe named parts of interfaces. It decouples authorship a bit (i.e., you're writing sections that become entries in a definition list), but that might be desirable, because you can use that structure produce other content (e.g., reuse across pages or editor tooltips) from the same source.

@wbamberg
Copy link
Collaborator Author

wbamberg commented May 5, 2021

I think this is the same thing I talked about upthread: #4367 (comment).

But two things, as noted in #4367 (comment):

  • that is some way off still
  • although that will reduce the number of <dl> elements we need, many such lists will be difficult to structure ever.

So we still need a way to represent <dl> in Markdown, now and probably quite far into the future as well.

@wbamberg
Copy link
Collaborator Author

wbamberg commented May 7, 2021

I'd like to try to resolve this, and unless more facts come to light, or unless @Gregoor thinks it's not practical to implement it, I'm in favour of the Kramdown syntax.

The main reason for this is that it's by a long way the most common Markdown syntax for <dl>, and the nearest thing to a standard. This has a few benefits:

  • people have put in the work to specify and implement it, and we can take advantage of that work
  • authors with Markdown experience are more likely to be familiar with it
  • if CommonMark does end up supporting <dl>, there's a reasonable chance that it will look like the Kramdown syntax, and there's virtually no chance it will look like any syntax we invent in this thread.

Please see also this thread about the possibility of adding <dl> support into CommonMark, which has lots of background about the Kramdown syntax: https://talk.commonmark.org/t/description-list/289.

I think it's a little misleading to say that the * deflist option does not require people to learn a new syntax because it is based on <ul>. It absolutely is a new syntax, because specifying <dl> is quite different structurally from <ul>. And we would have to define that syntax in detail for people to use it reliably - work that is already done for the Kramdown syntax.

One interesting note from the CommonMark discussion I linked above is that the Kramdown syntax is awkward to parse because you have to backtrack after finding the :this is the details element. To help at least a bit with this, I think it would be fine for our purposes to implement the following restriction on <dl>:

all children of a <dl> must be pairs of <dt> and <dd> elements. That is, you can't have more than one consecutive <dt>or more than one consecutive <dd>.

That would rule out:

<dl>
  <dt>A term</dt>
  <dt>Another term</dt>
  <dd>A definition</dd>
</dl>

...as well as:

<dl>
  <dt>A term</dt>
  <dd>A definition</dd>
  <dd>Another definition</dd>
</dl>
@sideshowbarker
Copy link
Collaborator

sideshowbarker commented May 8, 2021

if CommonMark does end up supporting <dl>, there's a reasonable chance that it will look like the Kramdown syntax, and there's virtually no chance it will look like any syntax we invent in this thread.

I agree that’s a pretty compelling reason (the possibility for forward compatibility).

To help at least a bit with this, I think it would be fine for our purposes to implement the following restriction on <dl>:

all children of a <dl> must be pairs of <dt> and <dd> elements. That is, you can't have more than one consecutive <dt>or more than one consecutive <dd>.

I wonder what instances (if any) we have in MDN of existing article using using more than one consecutive <dt>or more than one consecutive <dd>.

At least outside of MDN, it’s not hard to find quite legitimate cases of it — for example, I know it’s used in the markup for some specs (I think even in the HTML spec). It’s not uncommon to want to associate multiple (<dt>) terms with one shared block of text/description.

@sideshowbarker
Copy link
Collaborator

Here’s one example of adjacent-sibling <dt> from the HTML spec:

https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#the-htmlallcollection-interface%3Adom-htmlallcollection-item-2

element = collection . item(name)
collection = collection . item(name)
element = collection . namedItem(name)
collection = collection . namedItem(name)
element = collection(name)
collection = collection(name)
element = collection[name]
collection = collection[name]

Returns the item with ID or name name from the collection.

If there are multiple matching items, then an HTMLCollection object containing all those elements is returned.

Only button, form, iframe, input, map, meta, object, select, and textarea elements can have a name for the purpose of this method; their name is given by the value of their name attribute.

But that’s just one example among many such cases in the HTML spec — if you go to https://html.spec.whatwg.org and open the devtools console and do this:

document.querySelectorAll("dt + dt")

…that returns 425 nodes.

@sideshowbarker
Copy link
Collaborator

As far as instances of adjacent-sibling <dt> in existing MDN content, I ran this:

rg --multiline "</dt>\n+\s+<dt>" files/en-us/web | wc -l

…and that returns around 94 instances — though those are all concentrated in just 15 files:

files/en-us/web/accessibility/implementing_msaa_server/index.html
files/en-us/web/javascript/equality_comparisons_and_sameness/index.html
files/en-us/web/javascript/reference/global_objects/intl/pluralrules/resolvedoptions/index.html
files/en-us/web/javascript/reference/global_objects/intl/collator/resolvedoptions/index.html
files/en-us/web/javascript/reference/global_objects/intl/collator/compare/index.html
files/en-us/web/javascript/reference/global_objects/intl/numberformat/resolvedoptions/index.html
files/en-us/web/javascript/reference/global_objects/intl/datetimeformat/resolvedoptions/index.html
files/en-us/web/api/file_and_directory_entries_api/index.html
files/en-us/web/api/htmlscriptelement/index.html
files/en-us/web/http/proxy_servers_and_tunneling/proxy_auto-configuration_pac_file/index.html
files/en-us/web/html/inline_elements/index.html
files/en-us/web/css/color_value/index.html
files/en-us/web/css/align-items/index.html
files/en-us/web/css/custom-ident/index.html
files/en-us/web/css/list-style-type/index.html

https://github.com/mdn/content/blob/main/files/en-us/web/html/inline_elements/index.html#L102 has the largest number of them — but that’s an anomaly markup abuse. Not sure why it’s being used there, but it seems like it should be replaced with some other markup (regardless of the resolution of this issue).

https://github.com/mdn/content/blob/main/files/en-us/web/css/list-style-type/index.html has the next-largest number, and looks like a legitimate usage; see the rendered view at https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type#values — for example:

upper-alpha
upper-latin
  • Uppercase ASCII letters
  • E.g. A, B, C, … Z
  • upper-latin is unsupported in IE7 and earlier

There are a couple more CSS docs that have cases, and six JavaScript docs that do — including https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#when_to_use_jsxrefobject.is_versus_triple_equals:

{{jsxref("Math.atan2")}}
{{jsxref("Math.ceil")}}
{{jsxref("Math.pow")}}
{{jsxref("Math.round")}}
In some cases,it's possible for a -0 to be introduced into an expression as a return value of these methods even when no -0 exists as one of the parameters. For example, using {{jsxref("Math.pow")}} to raise {{jsxref("Infinity", "-Infinity")}} to the power of any negative, odd exponent evaluates to -0. Refer to the documentation for the individual methods.

Other examples:

@sideshowbarker
Copy link
Collaborator

After looking at the existing instances in MDN and at the instances in the HTML spec, I can’t really find any cases where it seems strictly necessary to mark them up with adjacent-sibling <dt>.

What I mean is that, in general, it seems like for any adjacent-sibling <dt> case, it’s possible to alternatively mark it up using one <dt> that holds multiple terms, separated by commas:

minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits
The values provided for these properties in the options argument or filled in as defaults. These properties are present only if neither minimumSignificantDigits nor maximumSignificantDigits was provided in the options argument.

I think a reasonable case could be made about that in fact being better for readability/usability as far as user experience.

To help at least a bit with this, I think it would be fine for our purposes to implement the following restriction on <dl>:

all children of a <dl> must be pairs of <dt> and <dd> elements. That is, you can't have more than one consecutive <dt>or more than one consecutive <dd>.

Given the above, it seems like it’d be OK/reasonable to impose such a restriction. The authoring guidance for it would seem to be pretty simple to document (e.g., “just separate multiple terms with commas on a single line, rather than putting them on separate lines”) — but ideally we’d want to be able to lint for it too. However, given the following:

One interesting note from the CommonMark discussion I linked above is that the Kramdown syntax is awkward to parse because you have to backtrack after finding the :this is the details element.

…then I’m guessing that trying to lint for it for it would run into the same problem. That is, if we could reliably parse for such cases to begin with, why would we not want to just allow them?

So I think it’d be fine to still impose the restriction even if it’s not something we could lint for.

@ddbeck
Copy link
Contributor

ddbeck commented May 10, 2021

Since there was more discussion about linting Kramdown-style lists, I spent more time trying to figure out where the sharp edges are with that (apart from parsing, which we've already acknowledged). I'm a bit troubled by them because I don't have a lot of ideas about how to sand down these edges (unlike the stuff we've considered for accepting the GFM table syntax or the solution we came up with notes and callouts). I'd really like some ideas because I don't have a lot of mitigation strategies that apply here.

CommonMark previews

In the one-level case, Kramdown style looks OK in GitHub:

Source

`callbackFn`
: Function to test for each element

`thisArg`
: Optional. Value to use as `this` when executing `callback`.

CommonMark rendering

callbackFn
: Function to test for each element

thisArg
: Optional. Value to use as this when executing callback.

The problem arises when you've got nested lists:

Source

`callbackFn`
: Function to test for each element

  `element`
  : The current element being processed in the typed array.

  `index`
  : The index of the current element being processed in the typed array.

  `array`
  : The typed array `some` was called upon.

    Here's a second paragraph.

`thisArg`
: Optional. Value to use as `this` when executing `callback`.

CommonMark rendering

callbackFn
: Function to test for each element

element
: The current element being processed in the typed array.

index
: The index of the current element being processed in the typed array.

array
: The typed array some was called upon.

Here's a second paragraph.

thisArg
: Optional. Value to use as this when executing callback.

It both flattens the list and turns subsequent blocks in the nested definitions into source blocks. This makes the preview in my editor unhelpful. As far as I can tell, there's no way I can stylesheet my way out of this problem. All my solutions to this problem are kinda poor and I'd like to hear ideas on how to counter this. It's really frustrating to me that there's no gentle upgrade path for opting into this one Kramdown feature (especially since we're not using Kramdown generally).

Conflict with Prettier

While experimenting, I also ran into this problem: Prettier really wants to wreck these lists (turns out I have Prettier formatting Markdown files by default). Over in #4325 (comment), we talked about the possibility of using Prettier to make working with GFM tables a bit nicer. This syntax puts a roadblock up for that. The easiest solution is to wrap those sections with Prettier opt-outs:

<!-- prettier-ignore-start -->
`callbackFn`
: Function to test for each element, taking three arguments.

  A second paragraph.

`thisArg`
: Optional. Value to use as `this` when executing `callback`.
<!-- prettier-ignore-end -->

Though that adds a weird, extra obligation for using these lists. (Prettier does support plugins, but it's not yet clear to me how easy it is to wrap the existing behavior and apply our special case.)

@wbamberg
Copy link
Collaborator Author

Daniel, Gregor and I just had a discussion about this issue, which I'm going to capture in this comment.

The concerns Daniel raised in #4367 (comment) are significant, especially the one about Prettier. What's worse is that they are symptoms of a general problem with adopting a "CommonMark-unfriendly" syntax: so much of the tooling tends to want CommonMark, that we're likely to encounter versions of this problem again, even if it were practical to fix the issue with Prettier.

So we should consider a principle that we try to stay "CommonMark-friendly" even when we are extending the CommonMark/GFM syntax, and that means reconsidering something like Daniel's deflist proposal in #4367 (comment).

The main reason to want to use Kramdown syntax would be that it's well-specified and has working implementations, so there's good reason to believe that the edge cases have been worked out. I'm a bit anxious to invent a new syntax and have to go through that. But perhaps we can make this less of a problem if we support a limited version of <dl>.

If we think we can get away with a syntax in which a <dl> consists of pairs of <dt>/<dd> elements (for which see Mike's #4367 (comment)), then it's much easier to spec and to write. At that point we might even consider that the * deflist magic word is over-specifying it, and we could even signal a <dl> using syntax like:

* `param1`
* : My description of param 1
* `param2`
* : My description of param 2

  It can have multiple paragraphs.

      ```js
      const thing = 1;
      ```

...that is, interpret a <ul> consisting of an even number of items, where even-numbered items start with ":", as a <dl>.

Or maybe for nicer GitHub formatting, something like:

* `param1`
    * : My description of param 1

* `param2`
    * : My description of param 2

      It can have multiple paragraphs, and code blocks too:

      ```js
      const thing = 1;
      ```

...which would render in GitHub like:


  • param1

    • : My description of param 1
  • param2

    • : My description of param 2

      It can have multiple paragraphs, and code blocks too:

      const thing = 1;

If authors really need full <dl> support, which as #4367 (comment) shows is really rare on MDN, they can always resort to HTML, as we already accept for tables.

@hamishwillee
Copy link
Collaborator

FWIW I really like the "nicer GitHub formatting" above #4367 (comment)

Other than the colon, this is how I represent definition lists in markdown normally.

@wbamberg
Copy link
Collaborator Author

I have had a go at writing this up -> #4987.

@Rumyra Rumyra added Content:JS JavaScript docs and removed needs triage Triage needed by staff and/or partners. Automatically applied when an issue is opened. labels Jun 7, 2021
@sideshowbarker
Copy link
Collaborator

I think this can either be closed or moved to the Discussions tracker?

@wbamberg
Copy link
Collaborator Author

Yeah, I left this open because I hadn't yet got around to updating the spec for the conversion: #3350 (comment) but I have done that now.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 31, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Content:JS JavaScript docs
8 participants