DatumHueEnterprise v0.0.24datumhue.com
Contents

Write a book

A book is pages of prose with live elements in them: buttons, charts, lists and slots your app fills. Write a page as data, show it in your app, then bind the elements that need to do something.

Write a page

Keep a book in a package, so it ships with the app that shows it. Make a folder trail for an app package, with this manifest as trail/package.kdl:

trail/package.kdlkdl
name "@alice/trail-guide"
version "0.1.0"
api_version "*"
kind "app"

A page is a .dhml file: Markdown prose, with elements in kdl fences. Pages live under chapters/ in a package. Save this one as trail/chapters/ridge.dhml:

trail/chapters/ridge.dhmldhml
# The ridge trail

The ridge trail climbs 640 m over 12 km. Start early: the
last bus leaves the top at five.

## Before you go

```kdl
column gap=6 {
    for var="thing" in="gear" {
        label "- {{ thing }}"
    }
    row gap=8 align_items="center" {
        button id="pack" "Packed one"
        label id="packed" "{{ packed }} of {{ #gear }} packed"
    }
}
```

## The climb

```kdl
chart id="climb" height=180 {
    line color="#7fbbb3" width=3 {
        point x=0 y=420
        point x=3 y=610
        point x=6 y=880
        point x=9 y=1010
        point x=12 y=1060
    }
}
```
  • Each element is a KDL node, named for the widget it makes: label, button, row, chart. DHML elements lists them all.
  • {{ }} holds a Lua expression, read from your app's globals each time the page renders. for repeats what it holds once per item of a list, and if shows what it holds when its condition is true.
  • An id names an element, so your app can reach it.

Show it in your app

Save this as trail/init.lua. It sets the globals the page reads, loads the page, mounts it, then binds the button:

trail/init.lualua
gear = { "Two liters of water", "A warm jacket", "A paper map" }
packed = 0

local dir = datumhue.package:dir()
local page = dir:open("chapters/ridge.dhml"):book()
page:mount({ height = "100%", padding = 16 })

page:element("pack"):on_click(function()
    packed = math.min(packed + 1, #gear)
    page:refresh("packed")
end)

local climb = page:element("climb")
climb:update({
    margin = { left = 48, right = 16, top = 8, bottom = 28 },
})
climb:axis({ scale = climb.x_scale, side = "bottom" })
climb:axis({ scale = climb.y_scale, side = "left", grid = true })

In this example: datumhue.package Package:dir Dir:open File:book Book:mount Book:element Book:refresh PlotArea:axis

Run it with datumhue run trail. The page shows its heading, the gear list, the button and the climb; each click on Packed one counts up.

  • File:book loads one page as a book. Book:mount shows it as a page that scrolls.
  • Book:element gives the handle the element made: a button's UiElement, a chart's PlotArea. Here the button gets a click handler, and the chart gets axes.
  • Book:refresh renders an element again, so its {{ }} read the globals as they are now.

Check it

Before you share the package, check it:

sh
$ datumhue pkg check trail

It prints a line that starts with OK when the manifest and the package's files are in order, and names each problem when they are not.

Make it a book of chapters

A book.kdl at the package's root names the book and lists its chapters, in reading order. Write the second page, trail/chapters/lake.dhml, as you wrote the first:

kdl
title "Trail guide"
chapter "The ridge trail" file="chapters/ridge.dhml"
chapter "The lake loop" file="chapters/lake.dhml"

Load the whole book with Dir:book instead of File:book. A book of several chapters mounts with a list of chapters beside the page and a row to step between them. Book:go moves to a chapter, or to an element's id on the page:

lua
local book = datumhue.package:dir():book()
book:mount({ height = "100%" })
book:go(2)

Add content_type "book" to package.kdl to say the package is a book. pkg check then also reads book.kdl, and checks every page it lists against the elements DatumHue knows.

Images

A page shows an image as Markdown writes one: !, its alt text in brackets, then its path in the book's folder in parentheses. A screen reader names the image by its alt text. Leave the brackets empty for an image that only decorates, such as a divider, and assistive technology passes over it:

dhml
![The lake at dawn, seen from the ridge](assets/lake.png)

![](assets/divider.png)

Images need a book loaded with Dir:book: File:book raises on a page with one. Bytes:book shows a named image as its alt text in italics, [image: The lake at dawn, seen from the ridge], and leaves out an image with empty brackets.

Scripts and listings

A lua fence in a page is a script: it runs once, in your app's globals, when the book loads. Add source after the fence's language to show its code instead of running it, or echo to show it and run it too. A kdl fence takes the same words:

dhml
```lua
gear = { "Water", "A hat" }
```

```kdl echo
button "Try me"
```

The font DatumHue draws text in joins some signs into one shape, such as >= into one sign. For a page that teaches code, mount the book with ligatures = false. Every character of the book's text then shows as typed: prose, listings, the text of elements, the chapter list and navigation alike. A chart's labels still join them.

lua
local book = datumhue.package:dir():book()
book:mount({ height = "100%", ligatures = false })

A book without scripts is data: pkg new with --kind book makes one as an asset pack, which any app opens with Dir:book. A book with scripts runs code, so its package must be an app; pkg check says so when it is not.

See also

Reference used on this page: datumhue.package Package:dir Dir:open File:book Book:mount Book:element Book:refresh PlotArea:axis Dir:book Book:go Bytes:book

datumhue.com

(c) 2026 Shuusei AB. Quote excerpts of this manual, with attribution to DatumHue, in your own documentation, tutorials, course materials, reviews, and other writing or video. Republishing it in whole or in substantial part needs written permission: support@datumhue.com.

Victor Mono is licensed under the SIL Open Font License 1.1
Copyright (c) 2024, Rune Bjørnerås (https://github.com/rubjo)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Try

    ↑↓ selectEnter openTab filter

    Settings

    Theme
    Text
    Print

    Keyboard shortcuts

    / or Ctrl KSearch
    ↑ ↓ EnterPick a result
    TabNext result filter
    j kNext or previous entry
    [ ]Previous or next page
    yCopy a link to the current entry
    ?These shortcuts
    EscClose

    On this page