@@ -0,0 +1,472 @@
7+Sail is a domain-specific language for composing HTML structures in Hoon.
8+Front-ends for Urbit apps are often created and uploaded separately to the rest
9+of the code in the desk. Sail provides an alternative approach, where front-ends
10+can be composed directly inside agents.
12+This document will walk through the basics of Sail and its syntax.
16+It’s easy to see how Sail can directly translate to HTML:
24+ ;meta(charset "utf-8");
30+ ; Welcome to my page.
33+ ;img@"https://media.urbit.org/docs/userspace/dist/wut.svg";
42+<html> <head> <title>My page</title> <meta charset="utf-8" />
43+</head> <body> <h1>Welcome!</h1> <p> Hello, world! Welcome to my
44+page. Here is an image: <br /> <img
45+src="https://media.urbit.org/docs/userspace/dist/wut.svg" /> </p> </body>
51+In Sail, tag heads are written with the tag name prepended by `;`. Unlike in
52+HTML, there are different ways of closing tags, depending on the needs of the
53+tag. One of the nice things about Hoon is that you don’t have to be constantly
54+closing expressions; Sail inherits this convenience.
58+Empty tags are closed with a `;` following the tag. For example, `;div;` will be
59+rendered as `<div></div>`. Non-container tags `;br;` and `;img@"some-url";` in
60+particular will be rendered as a single tag like `<br />` and `<img src="some-url" />`.
64+Filled tags are closed via line-break. To fill text inside, add `:` after the
65+tag name, then insert your plain text following a space. Example:
74+<h1>The title</h1>
79+To nest tags, simply create a new line. Nested tags need to be closed with `==`,
80+because they expect a list of sub-tags. If we nest lines of plain text with no
81+tag, the text will be wrapped in a `<p>` tag.
88+ This is some good content.
95+<body> <h1>Blog title</h1> <p>This is some good content.</p>
99+If we want to write a string with no tag at all, then we can prepend
100+those untagged lines with `;` and then a space:
113+<body> <h1>Welcome!</h1> Hello, world. We’re on the web. </body>
118+Adding attributes is simple: just add the desired attribute between parentheses,
119+right after the tag name without a space. We separate different attributes of
120+the same node by using `,`.
122+Attributes can also be specified in tall form, with each key prefixed by `=`,
123+followed by two spaces, and then a tape with its value. These two styles are
128+Wide-form attributes:
131+;div(title "a tooltip", style "color:red")
137+Tall-form attributes:
151+<div title="a tooltip" style="color:red"> <h1>Foo</h1> <p>foo bar
152+baz </p> </div>
157+Add `#` after tag name to add an ID:
166+<nav id="header">Menu</nav>
171+Add `.` after tag name to add a class:
180+<h1 class="text-blue">Title</h1>
183+If you want a class name that contains a space, you will need to use use the
184+syntax of a generic attribute:
187+;div(class "logo inverse");
193+<div class="logo inverse"></div>
198+Add `@` after the tag name to link your source:
207+<img src="example.png"/>
210+To add attributes to the image, like size specifications, add the desired
211+attribute after the `"` of the image name and before the final `;` of the `img`
215+;img@"example.png"(width "100%");
220+Add `/` after tag name to start an `href`.
223+;a/"urbit.org": A link to Urbit.org
229+<a href="urbit.org">A link to Urbit.org</a>
234+The textual content of tags, despite not being enclosed in double-quotes, are
235+actually tapes. This means they support interpolated Hoon expressions in the
236+usual manner. For example:
246+<p>foo ~2000.1.1 baz</p>
252+=/ txt=tape " bananas"
254+ ;b: {(a-co:co (mul 42 789))}
256+ {<our>} {<now>} {<`@ux`(end 6 eny)>}
263+<article> <b>33138</b> bananas <p>~zod ~2022.2.21..09.54.21..5b63
264+0x9827.99c7.06f4.8ef9 </p> </article>
269+The CSS for a page is usually quite large. The typical approach is to include a
270+separate arm in your agent (`++style` or the like) and write out the CSS in a
271+fenced cord block. You can then call `++trip` on it and include it in a style
285+ font-family: monospace;
290+And then your style tag might look like:
296+A cord is used rather than a tape so you don't need to escape double-quotes. The
297+[ketsig](/docs/hoon/reference/rune/ket#-ketsig) (`^~`) rune means `++trip` will
298+be run at compile time rather than call time.
302+So far we've shown rendered HTML for demonstrative purposes, but Sail syntax
303+doesn't directly produce HTML text. Instead, it produces a
304+[$manx](/docs/hoon/reference/stdlib/5e#manx). This is a Hoon type used to
305+represent an XML hierarchical structure with a single root node. There are six
306+XML-related types defined in the standard library:
309++$ mane $@(@tas [@tas @tas]) :: XML name+space
310++$ manx $~([[%$ ~] ~] [g=marx c=marl]) :: dynamic XML node
311++$ marl (list manx) :: XML node list
312++$ mars [t=[n=%$ a=[i=[n=%$ v=tape] t=~]] c=~] :: XML cdata
313++$ mart (list [n=mane v=tape]) :: XML attributes
314++$ marx $~([%$ ~] [n=mane a=mart]) :: dynamic XML tag
317+More information about these can be found in [section 5e of the standard library
318+reference](/docs/hoon/reference/stdlib/5e).
320+You don't need to understand these types in order to write Sail. The main thing
321+to note is that a `$manx` is a node (a single tag) and its contents is a
322+[$marl](/docs/hoon/reference/stdlib/5e#marl), which is just a `(list manx)`.
326+A `$manx` can be rendered as HTML in a tape with the `++en-xml:html` function in
327+`zuse.hoon`. For example:
331+[[%p ~] [[%$ [%$ "foobar"] ~] ~] ~]
338+> (crip (en-xml:html x))
342+You may wish to do this in your agent directly, but you can also leave mark
347+The `++en-xml:html` function will sanitize the contents of both attributes and
348+elements, converting characters such as `>` to HTML entities. For example:
351+> =z ;p(class "\"><script src=\"example.com/xxx.js"): <h1>FOO</h1>
353+> (crip (en-xml:html z))
354+'<p class=""><script src="example.com/xxx.js"><h1>FOO</h1></p>'
359+The mark for a raw `$manx` is a `%hymn`. This mark is included in the `%base`
360+desk. The mark for a rendered HTML cord is `%html`. The `%hymn` mark includes
361+conversion methods to both `%html` and `%mime`. The `%html` mark includes
362+conversion methods to `%mime` and `%hymn`.
364+If you want to produce an HTML document through Eyre's scry interface, for
365+example, you can either produce a `$manx` with a `%hymn` mark and let Eyre
366+convert it to `%html`, or else you can call `++en-xml:html` and then `++crip` on
367+the product of your Sail expressions and produce a cord with an `%html` mark
372+In addition to the syntax so far described, there are also a few Sail-specific
377+The [miclus rune](/docs/hoon/reference/rune/mic#-miclus) makes a `$marl` from a
378+complex hoon expression that produces a single `$manx`. Its main use is nesting
379+tall-form hoon logic in another Sail element. For example:
383+ ;b: {(a-co:co number)}
385+ ;+ ?: =(0 (mod number 2))
392+Produces one of these depending on the value of `number`:
395+<p> <b>2 </b>is an <b>even </b>number. </p>
399+<p> <b>12345 </b>is an <b>odd </b>number. </p>
404+The [micfas rune](/docs/hoon/reference/rune/mic#-micfas) turns an ordinary tape
405+into a `$manx`. For example:
408+> %- en-xml:html ;/ "foobar"
412+In order to nest it inside another Sail element, it must be preceeded with a
413+`;+` rune or similar, it cannot be used directly. For example:
421+The argument for Micfas needn't directly be a tape, it may also be a hoon
422+expression that produces a tape.
426+The [mictar rune](/docs/hoon/reference/rune/mic#-mictar) makes a `$marl` (a list
427+of XML nodes) from a complex hoon expression. This rune lets you add many
428+elements inside another Sail element. For example:
431+=/ nums=(list @ud) (gulf 1 9)
444+<p> <sub>1</sub><sup>2</sup><sub>3</sub>
445+<sup>4</sup><sub>5</sub><sup>6</sup>
446+<sub>7</sub><sup>8</sup><sub>9</sub> </p>
451+The [mictis rune](/docs/hoon/reference/rune/mic#-mictis) makes a `$marl` (a list
452+of XML nodes) from a series of `$manx`es. This is mostly useful if you want to
453+make the list outside of an element and then be able to insert it afterwards.
458+ ;= ;p: This is the first node.
459+ ;p: This is the second.
460+ ;p: Here is the last one.
470+<main> <p>This is the first node.</p> <p>This is the second.</p>
471+<p>Here is the last one.</p> </main>