# HSX

HSX (Hypertext S-expression) is a simple and powerful HTML (Living Standard)
generation library for Common Lisp.

This project is a fork of [ailisp/flute](https://github.com/ailisp/flute/).

## Warning

This software is still in ALPHA quality. The APIs are likely to change.

Please check the [release notes](https://code.skyizwhite.dev/paku/hsx/releases)
for updates.

## Getting Started

### 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
(hsx
  (div :id "example" :class "container"
    (h1 "Welcome to HSX")
    (p "This is an example paragraph.")))
```

This generates:

```html
<div id="example" class="container">
  <h1>Welcome to HSX</h1>
  <p>This is an example paragraph.</p>
</div>
```

To convert an HSX object into an HTML string, use the `render-to-string`
function:

```lisp
(render-to-string
  (hsx ...))
```

### Embedding Content

HSX allows you to embed Common Lisp forms directly within your HTML structure.

When working with HSX elements inside embedded Lisp forms, you should use the
`hsx` macro again.

```lisp
(hsx
  (div
    (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!")))))
```

This might generate:

```html
<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

To group multiple elements without adding an extra wrapper, use the fragment
`<>`.

```lisp
(hsx
  (<>
    (h1 "Grouped Elements")
    (p "First paragraph.")
    (p "Second paragraph.")))
```

This generates:

```html
<h1>Grouped Elements</h1>
<p>First paragraph.</p>
<p>Second paragraph.</p>
```

### Creating Components

You can define reusable components using the `defcomp` macro. Component names
must begin with a tilde (`~`). Properties should be declared using `&key`,
`&rest`, or both. The body must return an HSX element.

```lisp
(defcomp ~card (&key title children)
  (hsx
    (div :class "card"
      (h1 title)
      children)))
```

Alternatively, you can use a property list:

```lisp
(defcomp ~card (&rest props)
  (hsx
    (div :class "card"
      (h1 (getf props :title))
      (getf props :children))))
```

Usage example:

```lisp
(hsx
  (~card :title "Card Title"
    (p "This is a card component.")))
```

Generates:

```html
<div class="card">
  <h1>Card Title</h1>
  <p>This is a card component.</p>
</div>
```

## License

This project is licensed under the MIT License.

© 2024 Akira Tempaku

© 2018 Bo Yao