@@ -0,0 +1,472 @@
1++++
2+title = "Sail (HTML)"
3+weight = 6
4+template = "doc.html"
5++++
6+
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.
11+
12+This document will walk through the basics of Sail and its syntax.
13+
14+## Basic example
15+
16+It’s easy to see how Sail can directly translate to HTML:
17+
18+#### Sail code
19+
20+```hoon
21+;html
22+ ;head
23+ ;title: My page
24+ ;meta(charset "utf-8");
25+ ==
26+ ;body
27+ ;h1: Welcome!
28+ ;p
29+ ; Hello, world!
30+ ; Welcome to my page.
31+ ; Here is an image:
32+ ;br;
33+ ;img@"https://media.urbit.org/docs/userspace/dist/wut.svg";
34+ ==
35+ ==
36+==
37+```
38+
39+#### HTML code
40+
41+```html
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>
46+</html>
47+```
48+
49+## Tags and Closing
50+
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.
55+
56+### Empty
57+
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" />`.
61+
62+### Filled
63+
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:
66+
67+```hoon
68+;h1: The title
69+```
70+
71+Produces:
72+
73+```html
74+<h1>The title</h1>
75+```
76+
77+### Nested
78+
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.
82+
83+Example:
84+
85+```hoon
86+;body
87+ ;h1: Blog title
88+ This is some good content.
89+==
90+```
91+
92+Produces:
93+
94+```html
95+<body> <h1>Blog title</h1> <p>This is some good content.</p>
96+</body>
97+```
98+
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:
101+
102+```hoon
103+;body
104+ ;h1: Welcome!
105+ ; Hello, world!
106+ ; We’re on the web.
107+==
108+```
109+
110+Produces:
111+
112+```html
113+<body> <h1>Welcome!</h1> Hello, world. We’re on the web. </body>
114+```
115+
116+## Attributes
117+
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 `,`.
121+
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
124+shown below.
125+
126+### Generic
127+
128+Wide-form attributes:
129+
130+```hoon
131+;div(title "a tooltip", style "color:red")
132+ ;h1: Foo
133+ foo bar baz
134+==
135+```
136+
137+Tall-form attributes:
138+
139+```hoon
140+;div
141+ =title "a tooltip"
142+ =style "color:red"
143+ ;h1: Foo
144+ foo bar baz
145+==
146+```
147+
148+Produces:
149+
150+```html
151+<div title="a tooltip" style="color:red"> <h1>Foo</h1> <p>foo bar
152+baz </p> </div>
153+```
154+
155+### IDs
156+
157+Add `#` after tag name to add an ID:
158+
159+```hoon
160+;nav#header: Menu
161+```
162+
163+Produces:
164+
165+```html
166+<nav id="header">Menu</nav>
167+```
168+
169+### Classes
170+
171+Add `.` after tag name to add a class:
172+
173+```hoon
174+;h1.text-blue: Title
175+```
176+
177+Produces:
178+
179+```html
180+<h1 class="text-blue">Title</h1>
181+```
182+
183+If you want a class name that contains a space, you will need to use use the
184+syntax of a generic attribute:
185+
186+```hoon
187+;div(class "logo inverse");
188+```
189+
190+Produces:
191+
192+```html
193+<div class="logo inverse"></div>
194+```
195+
196+### Images
197+
198+Add `@` after the tag name to link your source:
199+
200+```hoon
201+;img@"example.png";
202+```
203+
204+Produces:
205+
206+```html
207+<img src="example.png"/>
208+```
209+
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`
212+tag:
213+
214+```hoon
215+;img@"example.png"(width "100%");
216+```
217+
218+### Links
219+
220+Add `/` after tag name to start an `href`.
221+
222+```hoon
223+;a/"urbit.org": A link to Urbit.org
224+```
225+
226+Produces:
227+
228+```html
229+<a href="urbit.org">A link to Urbit.org</a>
230+```
231+
232+## Interpolation
233+
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:
237+
238+```hoon
239+=| =time
240+;p: foo {<time>} bar
241+```
242+
243+Produces:
244+
245+```html
246+<p>foo ~2000.1.1 baz</p>
247+```
248+
249+Likewise:
250+
251+```hoon
252+=/ txt=tape " bananas"
253+;article
254+ ;b: {(a-co:co (mul 42 789))}
255+ ; {txt}
256+ {<our>} {<now>} {<`@ux`(end 6 eny)>}
257+==
258+```
259+
260+Produces:
261+
262+```html
263+<article> <b>33138</b> bananas <p>~zod ~2022.2.21..09.54.21..5b63
264+0x9827.99c7.06f4.8ef9 </p> </article>
265+```
266+
267+## A note on CSS
268+
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
272+tag. For example:
273+
274+```hoon
275+++ style
276+ ^~
277+ %- trip
278+ '''
279+ main {
280+ width: 100%;
281+ color: red;
282+ }
283+ header {
284+ color: blue;
285+ font-family: monospace;
286+ }
287+ '''
288+```
289+
290+And then your style tag might look like:
291+
292+```hoon
293+;style: {style}
294+```
295+
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.
299+
300+## Types and marks
301+
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:
307+
308+```hoon
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
315+```
316+
317+More information about these can be found in [section 5e of the standard library
318+reference](/docs/hoon/reference/stdlib/5e).
319+
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)`.
323+
324+### Rendering
325+
326+A `$manx` can be rendered as HTML in a tape with the `++en-xml:html` function in
327+`zuse.hoon`. For example:
328+
329+```
330+> ;p: foobar
331+[[%p ~] [[%$ [%$ "foobar"] ~] ~] ~]
332+
333+> =x ;p: foobar
334+
335+> (en-xml:html x)
336+"<p>foobar</p>"
337+
338+> (crip (en-xml:html x))
339+'<p>foobar</p>'
340+```
341+
342+You may wish to do this in your agent directly, but you can also leave mark
343+conversion to Eyre.
344+
345+### Sanitization
346+
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:
349+
350+```
351+> =z ;p(class "\"><script src=\"example.com/xxx.js"): <h1>FOO</h1>
352+
353+> (crip (en-xml:html z))
354+'<p class=""><script src="example.com/xxx.js"><h1>FOO</h1></p>'
355+```
356+
357+### Marks
358+
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`.
363+
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
368+directly.
369+
370+## Sail Runes
371+
372+In addition to the syntax so far described, there are also a few Sail-specific
373+runes:
374+
375+### `;+` Miclus
376+
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:
380+
381+```hoon
382+;p
383+ ;b: {(a-co:co number)}
384+ ; is an
385+ ;+ ?: =(0 (mod number 2))
386+ ;b: even
387+ ;b: odd
388+ ; number.
389+==
390+```
391+
392+Produces one of these depending on the value of `number`:
393+
394+```html
395+<p> <b>2 </b>is an <b>even </b>number. </p>
396+```
397+
398+```html
399+<p> <b>12345 </b>is an <b>odd </b>number. </p>
400+```
401+
402+### `;/` Micfas
403+
404+The [micfas rune](/docs/hoon/reference/rune/mic#-micfas) turns an ordinary tape
405+into a `$manx`. For example:
406+
407+```
408+> %- en-xml:html ;/ "foobar"
409+"foobar"
410+```
411+
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:
414+
415+```hoon
416+;p
417+ ;+ ;/ "foobar"
418+==
419+```
420+
421+The argument for Micfas needn't directly be a tape, it may also be a hoon
422+expression that produces a tape.
423+
424+### `;*` Mictar
425+
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:
429+
430+```hoon
431+=/ nums=(list @ud) (gulf 1 9)
432+;p
433+ ;* %+ turn nums
434+ |= n=@ud
435+ ?: =(0 (mod n 2))
436+ ;sup: {(a-co:co n)}
437+ ;sub: {(a-co:co n)}
438+==
439+```
440+
441+Produces:
442+
443+```html
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>
447+```
448+
449+### `;=` Mictis
450+
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.
454+For example:
455+
456+```hoon
457+=/ paras=marl
458+ ;= ;p: This is the first node.
459+ ;p: This is the second.
460+ ;p: Here is the last one.
461+ ==
462+;main
463+ ;* paras
464+==
465+```
466+
467+Produces:
468+
469+```html
470+<main> <p>This is the first node.</p> <p>This is the second.</p>
471+<p>Here is the last one.</p> </main>
472+```