2

For marking up source code beyond small fragments embedded in normal text, the official recommendation seems to be to use the code and pre elements. And this works fine if your listings should reflect source code exactly.

However, there is another style of listing, which was much more popular in the 1960s and 1970s than it is now. In this style, code is written using proportional typefaces instead of with monospaced typefaces. For example, here is an excerpt from §177 of TeX:

procedure print_glue(d : scaled; order : integer; s : str_number); { prints a glue component }
begin print_scaled(d);
if (order < normal)∨(order > filll) then print("foul")
else if order > normal then
   begin print("fil");
   while order > fil do
    begin print_char("l"); decr(order);
    end;
   end
  else if s ≠ 0 then print(s);
end;

(It doesn't look super good in sans serif.) I made the above completely non-semantically, using Unicode em spaces, thin spaces, and ** and * and `. In an actual HTML page, I would want to do something better.

The example is feasible to do with StackExchange's limited formatting capabilities, but some languages require far more complicated treatment. For instance, in Lisp code, the indentation varies with nested expressions, and certain things should be vertically aligned. This is best done with tables; pre does not provide any help for lining up text in a proportional typeface.

So how should a nicely formatted listing be expressed in HTML? Note: Markup for the lexical elements, such as identifiers and keywords, is not a problem.

Logically, code should be in code elements. But the idea that “The code element represents a fragment of computer code” might be violated, since a listing often represents more than just a fragment.

Using pre can have bad interactions with formatting of the HTML text for readability. And even though the listing is technically preformatted, it's not quite in the same sense as the HTML specification is using the term. Therefore I feel that not having pre is a reasonable idea.

Tables are the most general solution for dealing with indentation. In fact, there's an argument to be made that all listings represent a table consisting of a large column of lines. (Early assemblers divided input lines/cards into fields; code written for such assemblers would be best viewed in a tabular form. This is true for the first versions of Fortran as well.) I think that a table whose cells contain code elements could be a good choice; however, I don't know whether it would be better to have the table within a code element instead.

I'm ignoring line breaking, which is too complicated an issue to go into here. For now, assume that all line breaks have been determined in advance.


Here is some code that can't be formatted trivially, adapted from a Rosetta Code challenge solution. It's a Common Lisp function to calculate the Fast Fourier transform of a list of numbers:

(defun fft (x)
  (let ((length (length x)))
    (if (<= length 1)
        x
        (loop for (a b) on x by #'cddr
              collect a into as
              collect b into bs
              finally (let* ((ffta (fft as))
                             (fftb (fft bs))
                             (aux (loop for b in fftb
                                        and k from 0 by (/ pi length -1/2)
                                        collect (* b (cis k)))))
                        (return (mapcan #'mapcar '(+ -)
                                                 `(,ffta ,ffta)
                                                 `(,aux ,aux))))))))

The code in the “pretty” listing style looks like this:

The Lisp code above, but pretty-printed

I made it using plain TeX's tabbing macro \+. The TeX code is almost unreadable, but it would most likely be machine generated. Here's the code, for those familiar with TeX:

\+({\bf defun} {\it fft\/} ($x$)\cr
\+\quad&({\bf let} (({\it length\/} ({\it length\/} $x$)))\cr
\+&\quad&({\bf if} &($\le$ {\it length\/} 1)\cr
\+&&&$x$\cr
\+&&&({\bf loop} &{\bf for} ($a$ $b$) {\bf on} $x$ {\bf by} {\tt\#'}{\it cddr}\cr
\+&&&&{\bf collect} $a$ {\bf into} {\it as}\cr
\+&&&&{\bf collect} $b$ {\bf into} {\it bs}\cr
\+&&&&{\bf finally} &({\bf let}$*$ (&({\it ffta\/} ({\it fft\/} {\it as\/}))\cr
\+&&&&&&({\it fftb\/} ({\it fft\/} {\it bs\/}))\cr
\+&&&&&&({\it aux\/} ({\bf loop} &{\bf for} $b$ {\bf in} {\it fftb}\cr
\+&&&&&&&{\bf and} $k$ {\bf from} 0 {\bf by} ($\div$ $\pi$ {\it length\/} $-{1\over2}$)\cr
\+&&&&&&&{\bf collect} ($\times$ $b$ ({\it cis\/} $k$)))))\cr
\+&&&&&\quad({\bf return} ({\it mapcan\/} {\tt\#'}{\it mapcar\/} \cleartabs&{\tt'}(+ $-$)\cr
\+&&&&&&{\tt`}({\tt,}{\it ffta\/} {\tt,}{\it ffta\/})\cr
\+&&&&&&{\tt`}({\tt,}{\it aux\/} {\tt,}{\it aux\/}))))))))\cr
\bye

A possible translation into HTML is the following mess:

<!DOCTYPE html>
<html>
  <head>
    <style>
      table {
        display:inline-table;
        vertical-align:baseline;
        border-collapse: collapse;
      }
  
      .indent {
        margin-left:1em;
      }
  
      td {
        vertical-align:baseline
      }
    </style>
  </head>
  <body>
    <table>
      <tbody>
        <tr><td>(<b>defun</b> <i>fft</i> (<i>x</i>)</td></tr>
        <tr>
          <td>
            <table class="indent">
              <tbody>
                <tr><td>(<b>let</b> ((<i>length</i> (<i>length</i> <i>x</i>)))</td></tr>
                <tr>
                  <td>
                    <table class="indent">
                      <tbody>
                        <tr>
                          <td>(<b>if</b>
                            <table>
                              <tbody>
                                <tr><td>(&le; <i>length</i> 1)</td></tr>
                                <tr><td><i>x</i></td></tr>
                                <tr>
                                  <td>
                                    <table>
                                      <tbody>
                                        <tr>
                                          <td>
                                            (<b>loop</b>
                                            <table>
                                              <tbody>
                                                <tr><td><b>for</b> (<i>a</i> <i>b</i>) <b>on</b> <i>x</i> <b>by</b> #&rsquo;<i>cddr</i></td></tr>
                                                <tr><td><b>collect</b> <i>a</i> <b>into</b> <i>as</i></td></tr>
                                                <tr><td><b>collect</b> <i>b</i> <b>into</b> <i>bs</i></td></tr>
                                                <tr>
                                                  <td>
                                                    <b>finally</b>
                                                    <table>
                                                      <tbody>
                                                        <tr>
                                                          <td>
                                                            (<b>let</b>&lowast; (
                                                            <table>
                                                              <tbody>
                                                                <tr><td>(<i>ffta</i> (<i>fft</i> <i>as</i>))</td></tr>
                                                                <tr><td>(<i>fftb</i> (<i>fft</i> <i>bs</i>))</td></tr>
                                                                <tr>
                                                                  <td>
                                                                    (<i>aux</i> (<b>loop</b>
                                                                    <table>
                                                                      <tbody>
                                                                        <tr><td><b>for</b> <i>b</i> <b>in</b> <i>fftb</i></td></tr>
                                                                        <tr><td><b>and</b> <i>k</i> <b>from</b> 0 <b>by</b> (&divide; &pi; <i>length</i> &minus;1/2)</td></tr>
                                                                        <tr><td><b>collect</b> (&times; <i>b</i> (<i>cis</i> <i>k</i>)))))</td></tr>
                                                                      </tbody>
                                                                    </table>
                                                                  </td>
                                                                </tr>
                                                              </tbody>
                                                            </table>
                                                          </td>
                                                        </tr>
                                                        <tr>
                                                          <td>
                                                            <table class="indent">
                                                              <tbody>
                                                                <tr>
                                                                  <td>
                                                                    (<b>return</b> (<i>mapcan</i> #'(<i>mapcar</i>
                                                                    <table>
                                                                      <tbody>
                                                                        <tr><td>&rsquo;(+ &minus;)</td></tr>
                                                                        <tr><td>&lsquo;(,<i>ffta</i> ,<i>ffta</i>)</td></tr>
                                                                        <tr><td>&lsquo;(,<i>aux</i> ,<i>aux</i>)))))))))</td></tr>
                                                                      </tbody>
                                                                    </table>
                                                                  </td
                                                                </tr>
                                                              </tbody>
                                                            </table>
                                                          </td>
                                                        </tr>
                                                      </tbody>
                                                    </table>
                                                  </td>
                                                </tr>
                                              </tbody>
                                            </table
                                          </td>
                                        </tr
                                      </tbody>
                                    </table>
                                  </td>
                                </tr>
                               </tbody>
                             </table>
                           </td>
                         </tr>
                      </tbody>
                    </table>
                  </td>
                </tr>
              </tbody>
            </table>
          </td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

(The method of conversion from TeX to HTML here is based on a suggestion by David Carlisle. I've done it non-semantically again, for the sake of brevity.)

I'm not asking about how to format the code or how to make the code look nice. I'm asking about what elements are appropriate for containing the code.

10
  • 1
    This is an interesting question, although I am wondering how it might be answered within the context of UXSE since it relates to the specifications of HTML which is largely to do with implementation rather than something that can be designed. I'll have to think about this but hopefully there might be some people who can point you in the right direction. – Michael Lai Apr 12 at 1:40
  • 1
    @MichaelLai That's a good point; I wasn't entirely sure about posting it here. It seemed too subjective for StackOverflow, and there are some similar questions on this site. – texdr.aft Apr 12 at 2:16
  • 1
    I have not played with Markdown parsers enough to post an answer, but as an idea: would it not be enough to set the code snippets to a non-monospace one? Also, if I may ask: why? It seems to me monospace listing are overwhelmingly more popular for good reason... – Sergio A. Figueroa Apr 13 at 21:55
  • 1
    What are you trying to achieve by rendering this with straight HTML? Have you looked into MathJax (which is what StackExchange and many others use to solve this)? I'd think that's your best bet, especially since you say you can generate the TeX source automatically. I just don't think straight HTML is designed to address the very specific formatting requirements of this use case, so something with a bit more specialized formatting abilities like MathJax likely has the tools you're looking for. – maxathousand Apr 14 at 19:29
  • 1
    I'm still not sure what you're trying to achieve though. Are you trying to conform to the recommended usage of each HTML element, per the W3C standard? As far as I know, the W3C doesn't try to address this. Are you trying to render this in "plain text" in the browser as opposed to some 2-dimensional rendered area (e.g. <canvas>, <svg>, or <img>)? That's what I'm gathering, but I'm still not sure what you're trying to achieve with it. In other words, why do you want to force it into HTML instead of generating them with a tool that's designed for this? MathJax addresses accessibility also. – maxathousand Apr 14 at 20:59
0

using Table tabs is a mess, the data is not tabular. I suggest using tag because it is code. And tag because you want to retain line changes and white space. But use non-monospace font and or or tags for typography.

Not the answer you're looking for? Browse other questions tagged or ask your own question.