Digging Deeper

More info on the Hykup API.

Creating Tags with Functions

Macros are great for end-users, but not fantastic for writing composable code. Hykup includes a tag function which allows for tags to be created from a function rather than a macro.

Its signature is

(defn tag [tag-name &rest children &kwargs properties] ...)

The first argument is the tag name, which will be converted to a string with str. Positonal arguments will become the children to tag; they will be passed along unchanged. Keyword arguments will become the properties. They keywords themselves will automatically be unmangled. Both the keywords and the values will be converted to a string with str when the tag is rendered.

The tag function has no spacing rules. Spacing is preserved exactly as it was given.

A sample tag creation looks like:

(tag 'p :id 'my-fancy-p-tag
        :class "very-large very-small"
        (tag 'span "Text nodes")
        " are passed as strings.")
; <p id="my-fancy-p-tag" class="very-large very-small">
;   <span>Text nodes</span> are passed as strings</p>

Creating Custom Tags (Components)

Hykup would be close to useless if you weren’t able to create your own components. Luckily, you can!

Components are actually just functions. They accept a number of positional arguments, the last of which is the children to the component, as well as keyword arguments (the tag attributes). Classes are passed in as the class keyword, but this is likely to change in the future.

The functions must be named component- and then the name of the component. For instance, the component for the <p> tag is named component-p.

For examples:

; We'll define a "quote" component which adds fancy quotes before and after
; the inner text.
; We'll also accept attributes which will be placed on the tag.
(defn component-quote [children &kwargs attributes]
  ; (Dashes surpress whitespace)
  #kup [span #** ~properties / “ - ~@children - ”])

(.render #kup [quote :style "color: red" / I am quoted!])
; <span style="color: red">“I am quoted!”</span>

; But perhaps fancy quotes don't always work, and we want to
; be able to choose between normal and fancy quotes?
(defn component-quote [fancy? children &kwargs attributes]
  (if fancy?
    #kup [span #** ~attributes / “ - ~@children - ”]
    #kup [span #** ~attributes / " - ~@children - "]))

(.render #kup [quote ~false  ; Must unqote otherwise the function would be passed the
                    ; string "flase" rather than the value `false`.
            I have normal quotes])
; <span>"I have normal quotes"</span>

; Or maybe we want the caller to be able to supply their own
; start and end quotes, with defaults?
(defn component-quote [children
                       &kwonly [start-quote "“"] [end-quote "”"]
                       &kwargs attributes]
  #kup [span / ~start-quote - ~@children - ~end-quote]

(.render #kup [quote :start-quote [[ :end-quote ]] / I have custom quotes])
; <span>[[I have custom quotes]]</span>

Spacing Rules

If a Hykup form is supplied such as

#kup [p / I am some text]

There need to be some rules to decide how the symbols I am some text should be rendered. In this particular example, it’s simple: a space is placed between each symbol, rendering

<p>I am some text</p>

If children are nested elements or interpolated expressions, they are still surrounded with spaces:

(setv pronoun "I")
#kup [p / ~pronoun am [em / emphasized]]
; <p>I am <em>emphasized</em></em>

Strings allow for exact control of spaces:

#kup [p / "one two  three   four    "]
; <p>one two  three   four    </p>

but are still surrounded if placed next to other children:

#kup [p / "left" "middle" right]
; <p>left middle right</p>

Spaces may be explicitly surpressed with -:

#kup [p / "no" - "spaces" - "please"]
; </p>nospacesplease</p>

If text (symbol or string) starts with any of ,.;:)]}?!, a space will not implicitly be placed before it:

; Note that the comma after the em is parsed as its own symbol
#kup [p / well, well, [em / well], what do we have here?]
; <p>well, well, <em>well</em>, what do we have here?</p>

Similarly, if text (symbol or string) ends with any of ([{~#, a space will not implicitly be placed after it:

#kup [p / I own "~" (how-many-hamsters?) hamsters]
; <p>I own ~14 hamsters</p>