@@ -0,0 +1,477 @@
7+Sail is a domain-specific language for composing HTML (and XML) structures in
8+Hoon. Like everything else in Hoon, a Sail document is a noun, just one produced
9+by a specialized markup language within Hoon.
11+Front-ends for Urbit apps are often created and uploaded separately to the
12+rest of the code in the desk. Sail provides an alternative approach, where
13+front-ends can be composed directly inside agents.
15+This document will walk through the basics of Sail and its syntax.
19+It’s easy to see how Sail can directly translate to HTML:
21+<table><thead><tr><th>Sail</th><th>HTML</th></tr></thead>
22+<tbody><tr><td><pre class="language-hoon">
26+ ;meta(charset "utf-8");
32+ ; Welcome to my page.
40+&lt;html>
41+ &lt;head>
42+ &lt;title>My page&lt;/title>
43+ &lt;meta charset="utf-8" />
44+ &lt;/head>
45+ &lt;body>
46+ &lt;h1>Welcome!&lt;/h1>
47+ &lt;p>Hello, world! Welcome to my
48+ page. Here is an image:
49+ &lt;br />
50+ &lt;img src="/foo.png" />
51+ &lt;/p>
52+ &lt;/body>
53+&lt;/html>
54+</pre></td></tr></tbody></table>
58+In Sail, tag heads are written with the tag name prepended by `;`. Unlike in
59+HTML, there are different ways of closing tags, depending on the needs of the
60+tag. One of the nice things about Hoon is that you don’t have to constantly
61+close expressions; Sail inherits this convenience.
65+Empty tags are closed with a `;` following the tag. For example, `;div;` will be
66+rendered as `<div></div>`. Non-container tags `;br;` and `;img@"some-url";` in
67+particular will be rendered as a single tag like `<br />` and `<img src="some-url" />`.
71+Filled tags are closed via line-break. To fill text inside, add `:` after the
72+tag name, then insert your plain text following a space. Example:
75+| ---------------- | -------------------------- |
76+| `;h1: The title` | `<h1>The title</h1>` |
80+To nest tags, simply create a new line. Nested tags need to be closed with `==`,
81+because they expect a list of sub-tags.
83+If we nest lines of plain text with no tag, the text will be wrapped in a
84+`<p>` tag. Additionally, any text with atom auras or `++arm:syntax` in such
85+plain text lines will be wrapped in `<code>` tags.
89+<table><thead><tr><th>Sail</th><th>HTML</th></tr></thead><tbody><tr><td>
90+<pre class="language-hoon">
93+ This is some good content.
96+&lt;body>
97+ &lt;h1>Blog title&lt;/h1>
98+ &lt;p>This is some good content.&lt;/p>
99+&lt;/body>
100+</pre></td></tr></tbody></table>
102+If we want to write a string with no tag at all, then we can prepend
103+those untagged lines with `;` and then a space:
105+<table><thead><tr><th>Sail</th><th>HTML</th></tr></thead><tbody><tr><td>
106+<pre class="language-hoon">
113+&lt;body>
114+ &lt;h1>Welcome!&lt;/h1>
117+&lt;/body>
118+</pre></td></tr></tbody></table>
122+Adding attributes is simple: just add the desired attribute between parentheses,
123+right after the tag name without a space. We separate different attributes of
124+the same node by using `,`.
126+Attributes can also be specified in tall form, with each key prefixed by `=`,
127+followed by two spaces, and then a tape with its value. These two styles are
132+<table><thead><tr><th>Form</th><th>Example</th></tr></thead><tbody>
133+<tr><td>Wide</td><td><pre class="language-hoon">
134+;div(title "a tooltip", style "color:red")
139+<tr><td>Tall</td><td><pre class="language-hoon">
147+<tr><td>HTML</td><td><pre>
148+&lt;div title="a tooltip" style="color:red">
149+ &lt;h1>Foo&lt;/h1>
150+ &lt;p>foo bar baz &lt;/p>
151+&lt;/div>
152+</pre></td></tr></tbody></table>
156+Add `#` after tag name to add an ID:
159+| ------------------- | ----------------------------------- |
160+| `;nav#header: Menu` | `<nav id="header">Menu</nav>` |
164+Add `.` after tag name to add a class:
167+| ---------------------- | ---------------------------------------- |
168+| `;h1.text-blue: Title` | `<h1 class="text-blue">Title</h1>` |
170+For class values containing spaces, you can add additional `.`s like so:
173+| ------------------- | --------------------------------------- |
174+| `;div.foo.bar.baz;` | `<div class="foo bar baz"></div>` |
176+Otherwise, if your class value does not conform to the allowed `@tas`
177+characters, you must use the generic attribute syntax:
180+| ------------------------ | ----------------------------------- |
181+| `;div(class "!!! !!!");` | `<div class="!!! !!!"></div>` |
185+Add `@` after the tag name to link your source:
188+| --------------------- | ----------------------------- |
189+| `;img@"example.png";` | `<img src="example.png"/>` |
191+To add attributes to the image, like size specifications, add the desired
192+attribute after the `"` of the image name and before the final `;` of the `img`
193+tag like ;img@"example.png"(width "100%");`.
197+Add `/` after tag name to start an `href`.
199+<table><tr><th>Sail</th></tr><tr><td><pre class="language-hoon">
200+;a/"urbit.org": A link to Urbit.org
201+</pre></td></tr><tr><th>HTML</th></tr><tr><td><pre>
202+&lt;a href="urbit.org">A link to Urbit.org&lt;/a>
203+</pre></td></tr></table>
207+The textual content of tags, despite not being enclosed in double-quotes, are
208+actually tapes. This means they support interpolated Hoon expressions in the
209+usual manner. For example:
211+<table><thead><tr><th>Sail</th><th>HTML</th></tr></thead><tbody><tr><td>
212+<pre class="language-hoon">
214+;p: foo {&lt;time>} bar
216+&lt;p>foo ~2000.1.1 baz&lt;/p>
217+</pre></td></tr></tbody></table>
221+<table><tr><th>Sail</th></tr><tr><td><pre class="language-hoon">
222+=/ txt=tape " bananas"
224+ ;b: {(a-co:co (mul 42 789))}
226+ {&lt;our>} {&lt;now>} {&lt;`@ux`(end 6 eny)>}
228+</pre></td></tr><tr><th>HTML</th></tr><tr><td><pre>
229+&lt;article>
230+ &lt;b>33138&lt;/b> bananas
231+ &lt;p>~zod ~2022.2.21..09.54.21..5b63 0x9827.99c7.06f4.8ef9&lt;/p>
232+&lt;/article>
233+</pre></td></tr></table>
237+The CSS for a page is usually quite large. The typical approach is to include a
238+separate arm in your agent (`++style` or the like) and write out the CSS in a
239+fenced cord block. You can then call `++trip` on it and include it in a style
253+ font-family: monospace;
258+And then your style tag might look like:
264+A cord is used rather than a tape so you don't need to escape braces. The
265+[ketsig](/docs/hoon/reference/rune/ket#-ketsig) (`^~`) rune means `++trip` will
266+be run at compile time rather than call time.
270+So far we've shown rendered HTML for demonstrative purposes, but Sail syntax
271+doesn't directly produce HTML text. Instead, it produces a
272+[$manx](/docs/hoon/reference/stdlib/5e#manx). This is a Hoon type used to
273+represent an XML hierarchical structure with a single root node. There are six
274+XML-related types defined in the standard library:
277++$ mane $@(@tas [@tas @tas]) :: XML name+space
278++$ manx $~([[%$ ~] ~] [g=marx c=marl]) :: dynamic XML node
279++$ marl (list manx) :: XML node list
280++$ mars [t=[n=%$ a=[i=[n=%$ v=tape] t=~]] c=~] :: XML cdata
281++$ mart (list [n=mane v=tape]) :: XML attributes
282++$ marx $~([%$ ~] [n=mane a=mart]) :: dynamic XML tag
285+More information about these can be found in [section 5e of the standard library
286+reference](/docs/hoon/reference/stdlib/5e).
288+You don't need to understand these types in order to write Sail. The main thing
289+to note is that a `$manx` is a node (a single tag) and its contents is a
290+[$marl](/docs/hoon/reference/stdlib/5e#marl), which is just a `(list manx)`.
294+A `$manx` can be rendered as HTML in a tape with the `++en-xml:html` function in
295+`zuse.hoon`. For example:
299+[[%p ~] [[%$ [%$ "foobar"] ~] ~] ~]
306+> (crip (en-xml:html x))
312+The `++en-xml:html` function will sanitize the contents of both attributes and
313+elements, converting characters such as `>` to HTML entities. For example:
316+> =z ;p(class "\"><script src=\"example.com/xxx.js"): <h1>FOO</h1>
318+> (crip (en-xml:html z))
319+'<p class=""><script src="example.com/xxx.js"><h1>FOO</h1></p>'
324+There are a few different HTML and XML related marks, so it can be a bit
325+confusing. We'll look at the ones you're most likely to use.
331+This mark is used for HTML that has been printed as text in a cord. You may wish
332+to return this mark when serving pages to the web. To do so, you must run the
333+`$manx` produced by your Sail expressions through `++en-xml:html`, and then run
334+the resulting `tape` through `++crip`.
340+The `%hymn` mark is intended to be used for complete HTML documents - having an
341+`<html>` root element, `<head>`, `<body>`, etc. This isn't enforced on
342+the type level but it is assumed in certain mark conversion pathways. Eyre can
343+automatically convert a `%hymn` to printed `%html` if it was requested through
344+Eyre's scry interface.
350+The type of the `%elem` mark is a `$manx`, just like a `%hymn`. While `%hymn`s
351+are intended for complete HTML documents, `%elem`s are intended for more general
352+XML structures. You may wish to use an `%elem` mark if you're producing smaller
353+fragments of XML or HTML rather than whole documents. Like a `%hymn`, Eyre can
354+automatically convert it to `%html` if requested through its scry interface.
358+In general, if you're going to be composing web pages and serving them to web
359+clients, running the result of your Sail through `++en-xml:html`, `++crip`ping
360+it and producing `%html` is the most straight-forward approach. If you might
361+want to pass around a `$manx` to other agents or ships which may wish to
362+manipulate it futher, a `%hymn` or `%elem` is better.
366+In addition to the syntax so far described, there are also a few Sail-specific
371+The [miclus rune](/docs/hoon/reference/rune/mic#-miclus) makes a `$marl` from a
372+complex hoon expression that produces a single `$manx`. Its main use is nesting
373+tall-form hoon logic in another Sail element. For example:
377+ ;b: {(a-co:co number)}
379+ ;+ ?: =(0 (mod number 2))
386+Produces one of these depending on the value of `number`:
389+<p><b>2 </b>is an <b>even </b>number.</p>
393+<p><b>12345 </b>is an <b>odd </b>number.</p>
398+The [mictar rune](/docs/hoon/reference/rune/mic#-mictar) makes a `$marl` (a list
399+of XML nodes) from a complex hoon expression. This rune lets you add many
400+elements inside another Sail element. For example:
402+<table><thead><tr><th>Sail</th><th>HTML</th></tr></thead><tbody><tr><td>
403+<pre class="language-hoon">
404+=/ nums=(list @ud) (gulf 1 9)
413+&lt;p>
414+ &lt;sub>1&lt;/sub>&lt;sup>2&lt;/sup>
415+ &lt;sub>3&lt;/sub>&lt;sup>4&lt;/sup>
416+ &lt;sub>5&lt;/sub>&lt;sup>6&lt;/sup>
417+ &lt;sub>7&lt;/sub>&lt;sup>8&lt;/sup>
418+ &lt;sub>9&lt;/sub>
419+&lt;/p>
420+</pre></td></tr></tbody></table>
424+The [mictis rune](/docs/hoon/reference/rune/mic#-mictis) makes a `$marl` (a list
425+of XML nodes) from a series of `$manx`es. This is mostly useful if you want to
426+make the list outside of an element and then be able to insert it afterwards.
429+<table><thead><tr><th>Sail</th><th>HTML</th></tr></thead><tbody><tr><td>
430+<pre class="language-hoon">
440+&lt;main>
441+ &lt;p>First node.&lt;/p>
442+ &lt;p>Second node.&lt;/p>
443+ &lt;p>Third node.&lt;/p>
444+&lt;/main>
445+</pre></td></tr></tbody></table>
449+The [micfas rune](/docs/hoon/reference/rune/mic#-micfas) turns an ordinary tape
450+into a `$manx`. For example:
453+> %- en-xml:html ;/ "foobar"
457+In order to nest it inside another Sail element, it must be preceeded with a
458+`;+` rune or similar, it cannot be used directly. For example:
462+ ;+ ;/ ?: =(0 (mod eny 2))
470+Here's a couple of agents that make use of Sail, which you can use as a
473+- [Pals by ~palfun-foslup][pals]
474+- [Gora by ~rabsef-bicrym][gora]
476+[pals]: https://github.com/Fang-/suite/blob/master/app/pals/webui/index.hoon
477+[gora]: https://github.com/dalten-collective/gora/blob/master/sail/app/gora/goraui/index.hoon