hiccl

2024-10-12

HTML generator for Common Lisp

Upstream URL

github.com/garlic0x1/hiccl

Author

garlic0x1

License

MIT
README
Readme

HTML generator for Common Lisp

1Purpose

Spinneret is great but having to use macros to compose elements is less intuitive than simply having a function to render S-Expressions into HTML.

Hiccl also aims to be easy to extend with generic functions.

2Usage

Hiccl exposes one macro, `render`, which wraps `render-forms` to use &body args for editor support. It takes an output argument (same as format) and any number of SXML expressions.
  • Symbols are raw (you can write literal HTML between `|` symbols)
  • Strings are sanitized
  • Lists are HTML nodes

Outputting to `nil` returns a string.

NOTE: In the following examples, the HTML has been formatted for the readme. Hiccl does not pretty print it's output.

(hiccl:render nil
 `(:div :hi "world"
    (:span "tag can be symbol")
    "no </\"xss\"> allowed"
    |symbols <are> "raw" </are>|
    (:a :href "hTtPs://link.org" "clickme")))
<div hi="world">
  <span>
    tag can be symbol
  </span>
  no &lt;/&quot;xss&quot;&gt; allowed
  symbols <are> "raw" </are>
  <a href="hTtPs://link.org">clickme</a>
</div>

2.1JSX style syntax

class and id tags can use the following shorthand notation:
(hiccl:render nil
  '(:div.c1#id1.c2 :class "c3" :id "id2"))
<div class="c3 c1 c2" id="id2 id1">
</div>

Order is not guarunteed at the moment, but I will consider adding it.

2.2Specifying output

Render to a stream by specifying the `out` argument, renders to string if nil.
(hiccl:render *standard-output* sxml)
;; alternatively you can use `t` like in format
(hiccl:render t sxml)
;; to get a string
(hiccl:render nil sxml)

2.3Composing components

(defun hello-component (name)
  `(:span ,(format nil "Hello ~a" name)))

(hiccl:render nil
 `(:div
   "hello-component:"
   ,(hello-component "garlic")))
<div>
  hello-component:
  <span>Hello garlic</span>
</div>

2.4Boolean attributes

To use boolean attributes, just make its value nil:
(hiccl:render nil '(:div :bool nil))
<div bool>
</div>

2.5Extending the renderer

The renderer uses generic functions that you can extend to handle your objects:
(defclass thing ()
  ((a :initarg :a :accessor thing-a)
   (b :initarg :b :accessor thing-b)))

(defmethod hiccl::render-form (out (obj thing))
  (hiccl:render out
    `(:div.thing
      (:div.a ,(thing-a obj))
      (:div.b ,(thing-b obj)))))

(hiccl:render nil (make-instance 'thing :a "hi" :b "world"))
<div class="thing">
  <div class="a">
    hi
  </div>
  <div class="b">
    world
  </div>
</div>

You can also add special cases for HTML tags with methods:

(defmethod hiccl::apply-tag (out (tag (eql :my-comment)) body)
  (format out "<!--~%SPECIAL COMMENT~%~{~a~%~}-->" body))

(hiccl:render nil '(:my-comment "hi"))
<!--
SPECIAL COMMENT
hi
-->

These are not exported, so remember to use hiccl::* to refer to them.

Dependencies (7)

  • alexandria
  • cl-str
  • fiveam
  • lquery
  • plump
  • serapeum
  • trivia

Dependents (0)

    • GitHub
    • Quicklisp