Pollen tags to create X-expressions of sibling HTML tags

125 views
Skip to first unread message

dueck...@gmail.com

unread,
Aug 22, 2015, 4:39:57 PM8/22/15
to Pollen
As a kind of self-tutorial, I'm trying use Pollen to publish a test site that uses Tufte CSS, which seems like a good candidate for Pollen since some of the markup it requires is a bit ridiculous.

The markup Tufte CSS uses for sidenotes in particular is fussy:

<label for="sn-demo" class="margin-toggle sidenote-number"></label>
<input type="checkbox" id="sn-demo" class="margin-toggle"/>
<span class="sidenote">This is a numbered sidenote. The whole thing gets types inline with the body text.</span>

I thought I might implement this with a much simpler tag, numbered-note that would be used like this:

Now is the time for all good office men to come to the aid of the republic. ◊numbered-note["n1"]{This will be in a sidenote.} In other news, Subway is infested with rodents.

However, I'm having trouble with it;  I've tried several variations.

◊(define (numbered-note refid . text)
`(label [[for ,refid] [class "margin-toggle sidenote-number"]] )
`(input [[type "checkbox"] [id ,refid] [class "margin-toggle"]] )
`(span [(class "sidenote")] ,@text))

(At this early stage I'm defining everything right in the .pm file for simplicity) 
Using this definition, only the <span> tag actually appears in the HTML result, and I believe I understand why; these are actually three separate X-expressions so the function simply evaluates to the last one.

However, If I try various ways of enclosing all three expressions I get errors — either “contract violation” errors or errors that expect the input and span constructs to somehow be arguments to a function named label.

I think my ultimate problem is not knowing how to generate X-expressions that represent sibling tags. For example, even if I simplify to the following:

◊(define (numbered-note refid . words) '((strong "BOOM ") (em " slide")))

—then I get a contract violation error.

Matthew Butterick

unread,
Aug 22, 2015, 5:45:28 PM8/22/15
to dueck...@gmail.com, Pollen
You're correct about the problem. You're trying to insert three elements in place of one. Your current tag function is only returning the last element (because that's SOP for Racket). But even if you wrap your three return values in a `list`, which will make them behave as one unit, it still won't work (because a tag function expects to get back a single X-expression, and a list of X-expressions doesn't qualify).

Therefore two choices:

1) Wrap your three elements in a `span` tag rather than a `list`, and thereby make a single X-expression.

◊(define (numbered-note refid . text)
   `(span (label [[for ,refid] [class "margin-toggle sidenote-number"]] )
          (input [[type "checkbox"] [id ,refid] [class "margin-toggle"]] )
          (span [(class "sidenote")] ,@text)))

This technique uually works. But some things aren't supposed to go inside `span` tags (for example, block-level tags like `div`). It would be nice if there were a general grouping tag in HTML (like `g` in SVG or `begin` in Racket) but there's not.


2) If you really need to splice three elements in place of one, you need to perform your processing in the context of the parent tag. You can do this with `decode` (which knows to splice a return value that's a list). 

How? Rather than apply your processing directly as a tag function, you patch it in as a `decode` processor attached to `#:inline-txexpr-proc` as shown below. Notice that the `numbered-note` function inside `numbered-note-decoder` is the same as the tag function you wanted to write before. The only difference is that you're applying the processing "manually" (by checking the tag name) rather than "automatically" (by relying on a tag function).

This approach can be adapted for any tag function that needs to be replaced with multiple values (in fact I will put this example in the docs).

;;;;;;;;;;;;;;;;

#lang pollen/markup

Now is the time for all good office men to come to the aid of the republic. ◊numbered-note["n1"]{This will be in a sidenote.} In other news, Subway is infested with rodents.

◊(require pollen/decode txexpr)
◊(define (root . xs)
   (decode `(root ,@xs) #:inline-txexpr-proc numbered-note-decoder))

◊(define (numbered-note-decoder itx)

   (define (numbered-note refid . text)
     (list `(label [[for ,refid] [class "margin-toggle sidenote-number"]] )
           `(input [[type "checkbox"] [id ,refid] [class "margin-toggle"]] )
           `(span [(class "sidenote")] ,@text)))
   
   (if (eq? 'numbered-note (get-tag itx)) ; check if it's the tag we want
       (apply numbered-note (get-elements itx)) ; if so, apply processing
       itx)) ◊; if not, pass it through


;;;;;;;;;;;;;;;;;;


--
You received this message because you are subscribed to the Google Groups "Pollen" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pollenpub+...@googlegroups.com.
To post to this group, send email to poll...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pollenpub/ed6dfdb7-bdfc-4049-a9a1-124c64307d95%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

dueck...@gmail.com

unread,
Aug 22, 2015, 10:37:03 PM8/22/15
to Pollen, dueck...@gmail.com
Excellent response, thank you! Structurally it makes sense to me, and when reading through the docs I didn't realize this was another good use-case for decode. I'm now delving into the section on decode in the docs to make sure I better understand the details of your example solution. (Even though the span method would probably work in this specific example)
Reply all
Reply to author
Forward
0 new messages