aboutsummaryrefslogtreecommitdiff
path: root/docs/public/page
diff options
context:
space:
mode:
Diffstat (limited to 'docs/public/page')
-rw-r--r--docs/public/page/api/lustre.md195
-rw-r--r--docs/public/page/api/lustre/attribute.md290
-rw-r--r--docs/public/page/api/lustre/effect.md35
-rw-r--r--docs/public/page/api/lustre/element.md58
-rw-r--r--docs/public/page/api/lustre/element/html.md993
-rw-r--r--docs/public/page/api/lustre/element/svg.md462
-rw-r--r--docs/public/page/api/lustre/event.md146
-rw-r--r--docs/public/page/docs/components.md8
-rw-r--r--docs/public/page/docs/managing-state.md8
-rw-r--r--docs/public/page/docs/quickstart.md272
-rw-r--r--docs/public/page/docs/server-side-rendering.md8
-rw-r--r--docs/public/page/docs/side-effects.md8
-rw-r--r--docs/public/page/guides/mist.md8
-rw-r--r--docs/public/page/guides/wisp.md8
14 files changed, 2499 insertions, 0 deletions
diff --git a/docs/public/page/api/lustre.md b/docs/public/page/api/lustre.md
new file mode 100644
index 0000000..7ec5021
--- /dev/null
+++ b/docs/public/page/api/lustre.md
@@ -0,0 +1,195 @@
+# lustre
+
+## Applications
+
+### App | erlang javascript
+
+```gleam
+pub type App(flags, model, msg)
+```
+
+The `App` type represents all the parts that make up a Lustre program in the
+Model-View-Update architecture along with the runtime necessary to run it.
+
+Although the type itself is exposed to both the Erlang and JavaScript targets,
+the functions in this module to construct an `App` are only available in the
+JavaScript target, and `start` will only succeed when ran in the browser.
+
+In the future we may have a way to run Lustre applications on the backend, if
+you have any ideas on how to achieve this I'd love to hear about them!
+
+### Error | erlang javascript
+
+```gleam
+pub type Error {
+ AppAlreadyStarted
+ AppNotYetStarted
+ BadComponentName
+ ComponentAlreadyRegistered
+ ElementNotFound
+ NotABrowser
+}
+```
+
+The `Error` type represents all the ways that a Lustre program can fail. These
+include things like trying to start an application that has already been started,
+registering a component with a name that is not valid, or trying to start an
+application in a context that is not a browser.
+
+Often you will want to perform a couple of these actions together, and unifying
+the error type makes this easy. In many of the examples we `let assert` that the
+result is `Ok` but if you wanted to be a bit more dilligent you might use
+`result.try` instead:
+
+```gleam
+import gleam/result
+import lustre
+
+pub fn main () {
+ use _ <- result.try(lustre.component("my-component", ...))
+ let app = lustre.application(...)
+ use dispatch <- result.try(lustre.start(app, "[data-lustre-app]", Nil))
+
+ ...
+}
+```
+
+### element | javascript
+
+```gleam
+pub fn element(el: Element(msg)) -> App(Nil, Nil, msg)
+```
+
+An `element` application is the simplest kind of Lustre program. It takes an
+`Element` to render and renders it to the DOM. These applications hold no state
+and do not respond to messages, but that doesn't mean they are not interactive!
+
+It is possible for [`components`](#component) to be rendered inside an
+`element` application, and these components can be interactive with their own
+contained state and update loops.
+
+### simple | javascript
+
+```gleam
+pub fn simple(
+ init: fn(flags) -> model,
+ update: fn(model, msg) -> model,
+ view: fn(model) -> Element(msg)
+) -> App(flags, model, msg)
+```
+
+A `simple` program introduces the Model-View-Update architecture but leaves out
+the ability to dispatch side effects. This means your programs are interactive
+but cannot talk to the outside world.
+
+### application | javascript
+
+```gleam
+pub fn application(
+ init: fn(flags) -> #(model, Effect(msg)),
+ update: fn(model, msg) -> #(model, Effect(msg)),
+ view: fn(model) -> Element(msg)
+) -> App(flags, model, msg)
+```
+
+The `application` constructor is the most complete way to build a Lustre app. As
+with [`simple`](#simple) it uses the Model-View-Update architecture, but now your
+init and update functions can return side effects to be performed by the runtime
+in the form of an [`Effect`](/api/lustre/effect#effect-type).
+
+### start | javascript
+
+```gleam
+pub fn start(
+ app: App(flags, model, msg),
+ selector: String,
+ flags: flags,
+) -> Result(fn(msg) -> Nil, Error)
+```
+
+Start an application by providing a CSS selector to find the element to mount the
+application onto and any flags to pass to the application on first init. This
+function returns a `Result` and may fail for a number of reasons. Check out the
+[`Error`](#error-type) type for more information.
+
+### destroy | javascript
+
+```gleam
+pub fn destroy(app: App(flags, model, msg)) -> Result(Nil, Error)
+```
+
+Tear down a running application and remove it from the DOM. This can fail if the
+application has not yet been started.
+
+## Components
+
+Components take the same Model-View-Update building blocks used to create Lustre
+applications and allow them to be used as reusable stateful components. This is
+slightly different to how components are used in other frameworks like React
+where "component" refers more generally to any reusable piece of UI.
+
+In Lustre, functions that return an `Element` are known as "view functions" and
+components are more specific abstractions that encapsulate state and behaviour
+you might not want to deal with in your top-level application.
+
+Resist the urge to reach for components too early. The Elm community has managed
+to make do without components at all: you can get surprisingly far storing state
+in your top level application and passing it down to different view functions.
+This comes with the added benefit of it being much easier to reason about your
+UI as a whole.
+
+### component | javascript
+
+```gleam
+pub fn component(
+ name: String,
+ init: fn() -> #(model, Effect(msg)),
+ update: fn(model, msg) -> #(model, Effect(msg)),
+ view: fn(model) -> Element(msg),
+ on_attribute_change: Map(String, Decoder(msg)),
+) -> Result(Nil, Error)
+```
+
+Register a component with the runtime from the familiar Model-View-Update building
+blocks. Compared to an application, we have two additional arguments:
+
+- A name for the component. This name must follow the same rules laid out in the
+ [custom element spec](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name)
+ and should contain a hyphen (`-`) to avoid clashes with built-in HTML elements.
+- A map of attribute names to listen for changes to and a decoder for each to
+ decode those attributes into messages to send to your component's `update`
+ function.
+
+If it feels like the API for registering components is a little more verbose than
+you're used to, that's because it is! You can get surprisingly far storing state
+in your top level application and passing it down to different view functions
+without needing to use components at all. In fact, for communities like Elm this
+is the _only_ way to do things.
+
+## Utilities
+
+### is_browser | erlang javascript
+
+```gleam
+pub fn is_browser() -> Bool
+```
+
+Gleam has conditional compilation depending on whether you are targetting Erlang
+or JavaScript, but sometimes you want to be a bit more specific than that and
+check if you're running in the browser.
+
+This is a runtime check that will tell you just that. You could use this to create
+a view function that renders something simple on the backend but more complex or
+interactive on the frontend.
+
+### is_registered | erlang javascript
+
+```gleam
+pub fn is_registered(name: String) -> Bool
+```
+
+Lustre's components are built directly on the
+[custom element spec](https://html.spec.whatwg.org/multipage/custom-elements.html)
+which means they share the same global registery as other custom elements. This
+function can tell you if the name you want to use is already registered, by another
+Lustre component or otherwise.
diff --git a/docs/public/page/api/lustre/attribute.md b/docs/public/page/api/lustre/attribute.md
new file mode 100644
index 0000000..b23000b
--- /dev/null
+++ b/docs/public/page/api/lustre/attribute.md
@@ -0,0 +1,290 @@
+# lustre/attribute
+
+## Constructing attributes
+
+### Attribute | erlang javascript
+
+```gleam
+pub opaque type Attribute(msg)
+```
+
+### attribute | erlang javascript
+
+```gleam
+pub fn attribute(name: String, value: String) -> Attribute(msg)
+```
+
+### property | erlang javascript
+
+```gleam
+pub fn property(name: String, value: any) -> Attribute(msg)
+```
+
+### on | erlang javascript
+
+```gleam
+pub fn on(
+ name: String,
+ handler: fn(Dynamic) -> Result(msg, error)
+) -> Attribute(msg)
+```
+
+## Mapping attributes
+
+### map | erlang javascript
+
+```gleam
+pub fn map(attr: Attribute(a), f: fn(a) -> b) -> Attribute(b)
+```
+
+## Conversions
+
+### to_string | erlang javascript
+
+```gleam
+pub fn to_string(attr: Attribute(msg)) -> String
+```
+
+### to_string_builder | erlang javascript
+
+```gleam
+pub fn to_string_builder(attr: Attribute(msg)) -> StringBuilder
+```
+
+## Common attributes
+
+### style | erlang javascript
+
+```gleam
+pub fn style(properties: List(#(String, String))) -> Attribute(msg)
+```
+
+### class | erlang javascript
+
+```gleam
+pub fn class(name: String) -> Attribute(msg)
+```
+
+### classes | erlang javascript
+
+```gleam
+pub fn classes(names: List(#(String, Bool))) -> Attribute(msg)
+```
+
+### id | erlang javascript
+
+```gleam
+pub fn id(name: String) -> Attribute(msg)
+```
+
+## Input attributes
+
+### type\_ | erlang javascript
+
+```gleam
+pub fn type_(name: String) -> Attribute(msg)
+```
+
+### value | erlang javascript
+
+```gleam
+pub fn value(val: Dynamic) -> Attribute(msg)
+```
+
+### checked | erlang javascript
+
+```gleam
+pub fn checked(is_checked: Bool) -> Attribute(msg)
+```
+
+### placeholder | erlang javascript
+
+```gleam
+pub fn placeholder(text: String) -> Attribute(msg)
+```
+
+### selected | erlang javascript
+
+```gleam
+pub fn selected(is_selected: Bool) -> Attribute(msg
+```
+
+## More input attributes
+
+### accept | erlang javascript
+
+```gleam
+pub fn accept(types: List(String)) -> Attribute(msg)
+```
+
+### accept_charset | erlang javascript
+
+```gleam
+pub fn accept_charset(types: List(String)) -> Attribute(msg)
+```
+
+### msg | erlang javascript
+
+```gleam
+pub fn msg(uri: String) -> Attribute(msg)
+```
+
+### autocomplete | erlang javascript
+
+```gleam
+pub fn autocomplete(name: String) -> Attribute(msg)
+```
+
+### autofocus | erlang javascript
+
+```gleam
+pub fn autofocus(should_autofocus: Bool) -> Attribute(msg)
+```
+
+### disabled | erlang javascript
+
+```gleam
+pub fn disabled(is_disabled: Bool) -> Attribute(msg)
+```
+
+### name | erlang javascript
+
+```gleam
+pub fn name(name: String) -> Attribute(msg)
+```
+
+### pattern | erlang javascript
+
+```gleam
+pub fn pattern(regex: String) -> Attribute(msg)
+```
+
+### readonly | erlang javascript
+
+```gleam
+pub fn readonly(is_readonly: Bool) -> Attribute(msg)
+```
+
+### required | erlang javascript
+
+```gleam
+pub fn required(is_required: Bool) -> Attribute(msg)
+```
+
+### for | erlang javascript
+
+```gleam
+pub fn for(id: String) -> Attribute(msg)
+```
+
+## Range attributes
+
+### max | erlang javascript
+
+```gleam
+pub fn max(val: String) -> Attribute(msg)
+```
+
+### min | erlang javascript
+
+```gleam
+pub fn min(val: String) -> Attribute(msg)
+```
+
+### step | erlang javascript
+
+```gleam
+pub fn step(val: String) -> Attribute(msg)
+```
+
+## Textarea attributes
+
+### cols | erlang javascript
+
+```gleam
+pub fn cols(val: Int) -> Attribute(msg)
+```
+
+### rows | erlang javascript
+
+```gleam
+pub fn rows(val: Int) -> Attribute(msg)
+```
+
+### wrap | erlang javascript
+
+```gleam
+pub fn wrap(mode: String) -> Attribute(msg)
+```
+
+## Link attributes
+
+### href | erlang javascript
+
+```gleam
+pub fn href(uri: String) -> Attribute(msg)
+```
+
+### target | erlang javascript
+
+```gleam
+pub fn target(target: String) -> Attribute(msg)
+```
+
+### download | erlang javascript
+
+```gleam
+pub fn download(filename: String) -> Attribute(msg)
+```
+
+### rel | erlang javascript
+
+```gleam
+pub fn rel(relationship: String) -> Attribute(msg)
+```
+
+## Embedded content
+
+### gleam | erlang javascript
+
+```gleam
+pub fn src(uri: String) -> Attribute(msg)
+```
+
+### gleam | erlang javascript
+
+```gleam
+pub fn height(val: Int) -> Attribute(msg)
+```
+
+### gleam | erlang javascript
+
+```gleam
+pub fn width(val: Int) -> Attribute(msg)
+```
+
+### gleam | erlang javascript
+
+```gleam
+pub fn alt(text: String) -> Attribute(msg)
+```
+
+## Audio and video attributes
+
+### autoplay | erlang javascript
+
+```gleam
+pub fn autoplay(should_autoplay: Bool) -> Attribute(msg)
+```
+
+### controls | erlang javascript
+
+```gleam
+pub fn controls(visible: Bool) -> Attribute(msg)
+```
+
+### loop | erlang javascript
+
+```gleam
+pub fn loop(should_loop: Bool) -> Attribute(msg)
+```
diff --git a/docs/public/page/api/lustre/effect.md b/docs/public/page/api/lustre/effect.md
new file mode 100644
index 0000000..3a0bf11
--- /dev/null
+++ b/docs/public/page/api/lustre/effect.md
@@ -0,0 +1,35 @@
+# lustre/effect
+
+## Constructing Effects
+
+### Effect | erlang javascript
+
+```gleam
+pub opaque type Effect(msg)
+```
+
+### from | erlang javascript
+
+```gleam
+pub fn from(effect: fn(fn(msg) -> Nil) -> Nil) -> Effect(msg)
+```
+
+### none | erlang javascript
+
+```gleam
+pub fn none() -> Effect(msg)
+```
+
+### batch | erlang javascript
+
+```gleam
+pub fn batch(effects: List(Effect(msg))) -> Effect(msg)
+```
+
+## Manipulating Effects
+
+### map | erlang javascript
+
+```gleam
+pub fn map(effect: Effect(a), f: fn(a) -> b) -> Effect(b)
+```
diff --git a/docs/public/page/api/lustre/element.md b/docs/public/page/api/lustre/element.md
new file mode 100644
index 0000000..3510acd
--- /dev/null
+++ b/docs/public/page/api/lustre/element.md
@@ -0,0 +1,58 @@
+# lustre/element
+
+## Constructing elements
+
+### Element | erlang javascript
+
+```gleam
+pub opaque type Element(msg)
+```
+
+### element | erlang javascript
+
+```gleam
+pub fn element(
+ tag: String,
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### namespaced | erlang javascript
+
+```gleam
+pub fn namespaced(
+ namespace: String,
+ tag: String,
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### text | erlang javascript
+
+```gleam
+pub fn text(content: String) -> Element(msg)
+```
+
+## Mapping elements
+
+### map | erlang javascript
+
+```gleam
+pub fn map(element: Element(a), f: fn(a) -> b) -> Element(b)
+```
+
+## Conversions
+
+### to_string | erlang javascript
+
+```gleam
+pub fn to_string(element: Element(msg)) -> String
+```
+
+### to_string_builder | erlang javascript
+
+```gleam
+pub fn to_string_builder(element: Element(msg)) -> StringBuilder
+```
diff --git a/docs/public/page/api/lustre/element/html.md b/docs/public/page/api/lustre/element/html.md
new file mode 100644
index 0000000..883c66c
--- /dev/null
+++ b/docs/public/page/api/lustre/element/html.md
@@ -0,0 +1,993 @@
+# lustre/element/html
+
+## Main Root
+
+### html | erlang javascript
+
+```gleam
+pub fn html(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Document Metadata
+
+### base | erlang javascript
+
+```gleam
+pub fn base(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### head | erlang javascript
+
+```gleam
+pub fn head(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### link | erlang javascript
+
+```gleam
+pub fn link(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### meta | erlang javascript
+
+```gleam
+pub fn meta(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### style | erlang javascript
+
+```gleam
+pub fn style(attrs: List(Attribute(msg)), css: String) -> Element(msg)
+```
+
+### title | erlang javascript
+
+```gleam
+pub fn title(attrs: List(Attribute(msg)), content: String) -> Element(msg)
+```
+
+## Sectioning root
+
+### body | erlang javascript
+
+```gleam
+pub fn body(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Content sectioning
+
+### address | erlang javascript
+
+```gleam
+pub fn address(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### article | erlang javascript
+
+```gleam
+pub fn article(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### aside | erlang javascript
+
+```gleam
+pub fn aside(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### footer | erlang javascript
+
+```gleam
+pub fn footer(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### header | erlang javascript
+
+```gleam
+pub fn header(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### h1 | erlang javascript
+
+```gleam
+pub fn h1(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### h2 | erlang javascript
+
+```gleam
+pub fn h2(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### h3 | erlang javascript
+
+```gleam
+pub fn h3(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### h4 | erlang javascript
+
+```gleam
+pub fn h4(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### h5 | erlang javascript
+
+```gleam
+pub fn h5(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### h6 | erlang javascript
+
+```gleam
+pub fn h6(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### hgroup | erlang javascript
+
+```gleam
+pub fn hgroup(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### main | erlang javascript
+
+```gleam
+pub fn main(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### nav | erlang javascript
+
+```gleam
+pub fn nav(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### section | erlang javascript
+
+```gleam
+pub fn section(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### search | erlang javascript
+
+```gleam
+pub fn search(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Text content
+
+### blockquote | erlang javascript
+
+```gleam
+pub fn blockquote(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### dd | erlang javascript
+
+```gleam
+pub fn dd(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### div | erlang javascript
+
+```gleam
+pub fn div(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### dl | erlang javascript
+
+```gleam
+pub fn dl(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### dt | erlang javascript
+
+```gleam
+pub fn dt(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### figcaption | erlang javascript
+
+```gleam
+pub fn figcaption(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### figure | erlang javascript
+
+```gleam
+pub fn figure(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### hr | erlang javascript
+
+```gleam
+pub fn hr(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### li | erlang javascript
+
+```gleam
+pub fn li(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### menu | erlang javascript
+
+```gleam
+pub fn menu(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### ol | erlang javascript
+
+```gleam
+pub fn ol(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### p | erlang javascript
+
+```gleam
+pub fn p(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### pre | erlang javascript
+
+```gleam
+pub fn pre(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### ul | erlang javascript
+
+```gleam
+pub fn ul(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Inline text semantics
+
+### a | erlang javascript
+
+```gleam
+pub fn a(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### abbr | erlang javascript
+
+```gleam
+pub fn abbr(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### b | erlang javascript
+
+```gleam
+pub fn b(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### bdi | erlang javascript
+
+```gleam
+pub fn bdi(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### bdo | erlang javascript
+
+```gleam
+pub fn bdo(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### br | erlang javascript
+
+```gleam
+pub fn br(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### cite | erlang javascript
+
+```gleam
+pub fn cite(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### code | erlang javascript
+
+```gleam
+pub fn code(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### data | erlang javascript
+
+```gleam
+pub fn data(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### dfn | erlang javascript
+
+```gleam
+pub fn dfn(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### em | erlang javascript
+
+```gleam
+pub fn em(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### i | erlang javascript
+
+```gleam
+pub fn i(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### kbd | erlang javascript
+
+```gleam
+pub fn kbd(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### mark | erlang javascript
+
+```gleam
+pub fn mark(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### q | erlang javascript
+
+```gleam
+pub fn q(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### rp | erlang javascript
+
+```gleam
+pub fn rp(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### rt | erlang javascript
+
+```gleam
+pub fn rt(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### ruby | erlang javascript
+
+```gleam
+pub fn ruby(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### s | erlang javascript
+
+```gleam
+pub fn s(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### samp | erlang javascript
+
+```gleam
+pub fn samp(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### small | erlang javascript
+
+```gleam
+pub fn small(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### span | erlang javascript
+
+```gleam
+pub fn span(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### strong | erlang javascript
+
+```gleam
+pub fn strong(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### sub | erlang javascript
+
+```gleam
+pub fn sub(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### sup | erlang javascript
+
+```gleam
+pub fn sup(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### time | erlang javascript
+
+```gleam
+pub fn time(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### u | erlang javascript
+
+```gleam
+pub fn u(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### var | erlang javascript
+
+```gleam
+pub fn var(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### wbr | erlang javascript
+
+```gleam
+pub fn wbr(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## Image and multimedia
+
+### area | erlang javascript
+
+```gleam
+pub fn area(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### audio | erlang javascript
+
+```gleam
+pub fn audio(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### img | erlang javascript
+
+```gleam
+pub fn img(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### map | erlang javascript
+
+```gleam
+pub fn map(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### track | erlang javascript
+
+```gleam
+pub fn track(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### video | erlang javascript
+
+```gleam
+pub fn video(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Embedded content
+
+### embed | erlang javascript
+
+```gleam
+pub fn embed(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### iframe | erlang javascript
+
+```gleam
+pub fn iframe(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### object | erlang javascript
+
+```gleam
+pub fn object(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### picture | erlang javascript
+
+```gleam
+pub fn picture(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### portal | erlang javascript
+
+```gleam
+pub fn portal(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### source | erlang javascript
+
+```gleam
+pub fn source(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## SVG and MathML
+
+### svg | erlang javascript
+
+````gleam
+pub fn svg(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+
+### math | erlang javascript
+
+```gleam
+pub fn math(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+````
+
+## Scripting
+
+### canvas | erlang javascript
+
+```gleam
+pub fn canvas(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### noscript | erlang javascript
+
+```gleam
+pub fn noscript(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### script | erlang javascript
+
+```gleam
+pub fn script(attrs: List(Attribute(msg)), js: String) -> Element(msg)
+```
+
+## Demarcating edits
+
+### del | erlang javascript
+
+```gleam
+pub fn del(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### ins | erlang javascript
+
+```gleam
+pub fn ins(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Table content
+
+### caption | erlang javascript
+
+```gleam
+pub fn caption(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### col | erlang javascript
+
+```gleam
+pub fn col(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### colgroup | erlang javascript
+
+```gleam
+pub fn colgroup(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### table | erlang javascript
+
+```gleam
+pub fn table(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### tbody | erlang javascript
+
+```gleam
+pub fn tbody(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### td | erlang javascript
+
+```gleam
+pub fn td(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### tfoot | erlang javascript
+
+```gleam
+pub fn tfoot(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### th | erlang javascript
+
+```gleam
+pub fn th(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### thead | erlang javascript
+
+```gleam
+pub fn thead(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### tr | erlang javascript
+
+```gleam
+pub fn tr(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Forms
+
+### button | erlang javascript
+
+```gleam
+pub fn button(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### datalist | erlang javascript
+
+```gleam
+pub fn datalist(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### fieldset | erlang javascript
+
+```gleam
+pub fn fieldset(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### form | erlang javascript
+
+```gleam
+pub fn form(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### input | erlang javascript
+
+```gleam
+pub fn input(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### label | erlang javascript
+
+```gleam
+pub fn label(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### legend | erlang javascript
+
+```gleam
+pub fn legend(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### meter | erlang javascript
+
+```gleam
+pub fn meter(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### optgroup | erlang javascript
+
+```gleam
+pub fn optgroup(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### option | erlang javascript
+
+```gleam
+pub fn option(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### output | erlang javascript
+
+```gleam
+pub fn output(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### progress | erlang javascript
+
+```gleam
+pub fn progress(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### select | erlang javascript
+
+```gleam
+pub fn select(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### textarea | erlang javascript
+
+```gleam
+pub fn textarea(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## Interactive elements
+
+### details | erlang javascript
+
+```gleam
+pub fn details(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### dialog | erlang javascript
+
+```gleam
+pub fn dialog(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### summary | erlang javascript
+
+```gleam
+pub fn summary(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Web components
+
+### slot | erlang javascript
+
+```gleam
+pub fn slot(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### template | erlang javascript
+
+```gleam
+pub fn template(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
diff --git a/docs/public/page/api/lustre/element/svg.md b/docs/public/page/api/lustre/element/svg.md
new file mode 100644
index 0000000..a7a0ec2
--- /dev/null
+++ b/docs/public/page/api/lustre/element/svg.md
@@ -0,0 +1,462 @@
+# lustre/element/svg
+
+## Animation elements
+
+### animate | erlang javascript
+
+```gleam
+pub fn animate(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### animate_motion | erlang javascript
+
+```gleam
+pub fn animate_motion(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### animate_transform | erlang javascript
+
+```gleam
+pub fn animate_transform(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### mpath | erlang javascript
+
+```gleam
+pub fn mpath(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### set | erlang javascript
+
+```gleam
+pub fn set(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## Basic shapes
+
+### circle | erlang javascript
+
+```gleam
+pub fn circle(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### ellipse | erlang javascript
+
+```gleam
+pub fn ellipse(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### line | erlang javascript
+
+```gleam
+pub fn line(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### polygon | erlang javascript
+
+```gleam
+pub fn polygon(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### polyline | erlang javascript
+
+```gleam
+pub fn polyline(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### rect | erlang javascript
+
+```gleam
+pub fn rect(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## Container elements
+
+### a | erlang javascript
+
+```gleam
+pub fn a(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### defs | erlang javascript
+
+```gleam
+pub fn defs(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### g | erlang javascript
+
+```gleam
+pub fn g(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### marker | erlang javascript
+
+```gleam
+pub fn marker(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### mask | erlang javascript
+
+```gleam
+pub fn mask(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### missing_glyph | erlang javascript
+
+```gleam
+pub fn missing_glyph(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### pattern | erlang javascript
+
+```gleam
+pub fn pattern(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### svg | erlang javascript
+
+```gleam
+pub fn svg(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### switch | erlang javascript
+
+```gleam
+pub fn switch(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### symbol | erlang javascript
+
+```gleam
+pub fn symbol(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Descriptive elements
+
+### desc | erlang javascript
+
+```gleam
+pub fn desc(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### metadata | erlang javascript
+
+```gleam
+pub fn metadata(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### title | erlang javascript
+
+```gleam
+pub fn title(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+## Filter effects
+
+### fe_blend | erlang javascript
+
+```gleam
+pub fn fe_blend(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_color_matrix | erlang javascript
+
+```gleam
+pub fn fe_color_matrix(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_component_transfer | erlang javascript
+
+```gleam
+pub fn fe_component_transfer(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_composite | erlang javascript
+
+```gleam
+pub fn fe_composite(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_convolve_matrix | erlang javascript
+
+```gleam
+pub fn fe_convolve_matrix(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_diffuse_lighting | erlang javascript
+
+```gleam
+pub fn fe_diffuse_lighting(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### fe_displacement_map | erlang javascript
+
+```gleam
+pub fn fe_displacement_map(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_drop_shadow | erlang javascript
+
+```gleam
+pub fn fe_drop_shadow(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_flood | erlang javascript
+
+```gleam
+pub fn fe_flood(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_func_a | erlang javascript
+
+```gleam
+pub fn fe_func_a(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_func_b | erlang javascript
+
+```gleam
+pub fn fe_func_b(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_func_g | erlang javascript
+
+```gleam
+pub fn fe_func_g(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_func_r | erlang javascript
+
+```gleam
+pub fn fe_func_r(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_gaussian_blur | erlang javascript
+
+```gleam
+pub fn fe_gaussian_blur(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_image | erlang javascript
+
+```gleam
+pub fn fe_image(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_merge | erlang javascript
+
+```gleam
+pub fn fe_merge(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### fe_merge_node | erlang javascript
+
+```gleam
+pub fn fe_merge_node(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_morphology | erlang javascript
+
+```gleam
+pub fn fe_morphology(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_offset | erlang javascript
+
+```gleam
+pub fn fe_offset(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_specular_lighting | erlang javascript
+
+```gleam
+pub fn fe_specular_lighting(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### fe_tile | erlang javascript
+
+```gleam
+pub fn fe_tile(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### fe_turbulence | erlang javascript
+
+```gleam
+pub fn fe_turbulence(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## Gradient elements
+
+### linear_gradient | erlang javascript
+
+```gleam
+pub fn linear_gradient(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### radial_gradient | erlang javascript
+
+```gleam
+pub fn radial_gradient(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### stop | erlang javascript
+
+```gleam
+pub fn stop(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## Graphical elements
+
+### image | erlang javascript
+
+```gleam
+pub fn image(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### path | erlang javascript
+
+```gleam
+pub fn path(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### text | erlang javascript
+
+```gleam
+pub fn text(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### use\_ | erlang javascript
+
+```gleam
+pub fn use_(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## Lighting elements
+
+### fe_distant_light | erlang javascript
+
+```gleam
+pub fn fe_distant_light(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_point_light | erlang javascript
+
+```gleam
+pub fn fe_point_light(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+### fe_spot_light | erlang javascript
+
+```gleam
+pub fn fe_spot_light(attrs: List(Attribute(msg))) -> Element(msg)
+```
+
+## Non-rendered elements
+
+### clip_path | erlang javascript
+
+```gleam
+pub fn clip_path(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### script | erlang javascript
+
+```gleam
+pub fn script(attrs: List(Attribute(msg)), js: String) -> Element(msg)
+```
+
+### style | erlang javascript
+
+```gleam
+pub fn style(attrs: List(Attribute(msg)), css: String) -> Element(msg)
+```
+
+## Renderable elements
+
+### foreign_object | erlang javascript
+
+```gleam
+pub fn foreign_object(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### text_path | erlang javascript
+
+```gleam
+pub fn text_path(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
+
+### tspan | erlang javascript
+
+```gleam
+pub fn tspan(
+ attrs: List(Attribute(msg)),
+ children: List(Element(msg)),
+) -> Element(msg)
+```
diff --git a/docs/public/page/api/lustre/event.md b/docs/public/page/api/lustre/event.md
new file mode 100644
index 0000000..695cb7b
--- /dev/null
+++ b/docs/public/page/api/lustre/event.md
@@ -0,0 +1,146 @@
+# lustre/event
+
+## Mouse events
+
+### on_click | erlang javascript
+
+```gleam
+pub fn on_click(msg: msg) -> Attribute(msg)
+```
+
+### on_mouse_down | erlang javascript
+
+```gleam
+pub fn on_mouse_down(msg: msg) -> Attribute(msg)
+```
+
+### on_mouse_up | erlang javascript
+
+```gleam
+pub fn on_mouse_up(msg: msg) -> Attribute(msg)
+```
+
+### on_mouse_enter | erlang javascript
+
+```gleam
+pub fn on_mouse_enter(msg: msg) -> Attribute(msg)
+```
+
+### on_mouse_leave | erlang javascript
+
+```gleam
+pub fn on_mouse_leave(msg: msg) -> Attribute(msg)
+```
+
+### on_mouse_over | erlang javascript
+
+```gleam
+pub fn on_mouse_over(msg: msg) -> Attribute(msg)
+```
+
+### on_mouse_out | erlang javascript
+
+```gleam
+pub fn on_mouse_out(msg: msg) -> Attribute(msg)
+```
+
+## Keyboard events
+
+### on_keypress | erlang javascript
+
+```gleam
+pub fn on_keypress(msg: fn(String) -> msg) -> Attribute(msg)
+```
+
+### on_keydown | erlang javascript
+
+```gleam
+pub fn on_keydown(msg: fn(String) -> msg) -> Attribute(msg)
+```
+
+### on_keyup | erlang javascript
+
+```gleam
+pub fn on_keyup(msg: fn(String) -> msg) -> Attribute(msg)
+```
+
+## Form messages
+
+### on_input | erlang javascript
+
+```gleam
+pub fn on_input(msg: fn(String) -> msg) -> Attribute(msg)
+```
+
+### on_change | erlang javascript
+
+```gleam
+pub fn on_change(msg: fn(Bool) -> msg) -> Attribute(msg)
+```
+
+### on_submit | erlang javascript
+
+```gleam
+pub fn on_submit(msg: msg) -> Attribute(msg)
+```
+
+## Focus events
+
+### on_focus | erlang javascript
+
+```gleam
+pub fn on_focus(msg: msg) -> Attribute(msg)
+```
+
+### on_blur | erlang javascript
+
+```gleam
+pub fn on_blur(msg: msg) -> Attribute(msg)
+```
+
+## Custom events
+
+### on | erlang javascript
+
+```gleam
+pub fn on(
+ name: String,
+ handler: fn(Dynamic) -> Result(msg, error)
+) -> Attribute(msg)
+```
+
+### prevent_default | javascript
+
+```gleam
+pub fn prevent_default(event: Dynamic) -> Nil
+```
+
+### stop_propagation | javascript
+
+```gleam
+pub fn stop_propagation(event: Dynamic) -> Nil
+```
+
+### value | erlang javascript
+
+```gleam
+pub fn value(event: Dynamic) -> Decoder(String)
+```
+
+### checked | erlang javascript
+
+```gleam
+pub fn checked(event: Dynamic) -> Decoder(Bool)
+```
+
+### mouse_position | erlang javascript
+
+```gleam
+pub fn mouse_position(event: Dynamic) -> Decoder(#(Float, Float))
+```
+
+### emit | javascript
+
+```gleam
+pub fn emit(event: String, data: any) -> Effect(msg)
+```
diff --git a/docs/public/page/docs/components.md b/docs/public/page/docs/components.md
new file mode 100644
index 0000000..6fecf43
--- /dev/null
+++ b/docs/public/page/docs/components.md
@@ -0,0 +1,8 @@
+# Components
+
+Whoopsie, I haven't got round to writing this guide yet. If you haven't checked
+out the [quickstart guide](/docs/quickstart) that is probably the best place to
+go to get up to speed.
+
+If you have any questions, feel free to ping `@hayleigh.dev` over on the Gleam
+[Discord server](https://discord.gg/Fm8Pwmy) and I'd be happy to help you out!
diff --git a/docs/public/page/docs/managing-state.md b/docs/public/page/docs/managing-state.md
new file mode 100644
index 0000000..7da5449
--- /dev/null
+++ b/docs/public/page/docs/managing-state.md
@@ -0,0 +1,8 @@
+# Managing state
+
+Whoopsie, I haven't got round to writing this guide yet. If you haven't checked
+out the [quickstart guide](/docs/quickstart) that is probably the best place to
+go to get up to speed.
+
+If you have any questions, feel free to ping `@hayleigh.dev` over on the Gleam
+[Discord server](https://discord.gg/Fm8Pwmy) and I'd be happy to help you out!
diff --git a/docs/public/page/docs/quickstart.md b/docs/public/page/docs/quickstart.md
new file mode 100644
index 0000000..f706478
--- /dev/null
+++ b/docs/public/page/docs/quickstart.md
@@ -0,0 +1,272 @@
+# Quickstart
+
+Lustre is a frontend web framework for Gleam. It is primarily focused on helping
+you build robust single-page applications (SPAs), but it can also be used on the
+server to render static HTML. To get an idea of what it's all about, here's a
+quick overview of Lustre's key features:
+
+- Elm-inspired runtime with state management and controlled side effects out of
+ the box.
+- A simple, declarative API for building type-safe user interfaces.
+- Stateful components built as custom elements and useable just like any other
+ HTML element.
+- Static HTML rendering anywhere Gleam can run: the BEAM, Node.js, Deno, or the
+ browser.
+
+In this quickstart guide we'll take a look at how to get up and running with
+Lustre in both the browser and on the server.
+
+## In the browser | javascript
+
+To get started, we'll scaffold a new Gleam project using `gleam new`. If you've
+found your way to this guide but don't already know what Gleam is you can read
+about it over at [gleam.run](https://gleam.run).
+
+```shell
+$ gleam new lustre_quickstart && gleam add lustre
+```
+
+In a real project you probably want to use a build tool like [vite](https://vitejs.dev)
+along with the [vite-gleam](https://github.com/Enderchief/vite-gleam) plugin, but
+to keep this guide simple we'll just show you what code you need to write and leave
+the details on serving the app up to you. MDN have a handy guide covering some
+different options to [set up a local web server for development](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Tools_and_setup/set_up_a_local_testing_server)
+if you need some ideas.
+
+### Basic HTML setup
+
+With our Gleam project scaffolded, go ahead and create an `index.html` in the root
+of the project. This is the minimal code you'll typically want to get started:
+
+```html
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Lustre Quickstart</title>
+
+ <script type="module">
+ import { main } from "./build/dev/javascript/lustre_quickstart/app.mjs";
+
+ document.addEventListener("DOMContentLoaded", () => {
+ main();
+ });
+ </script>
+ </head>
+
+ <body>
+ <div data-lustre-app></div>
+ </body>
+</html>
+```
+
+We wait until the DOM has loaded before calling the our app's `main` function.
+This will mount the Lustre app and start rendering. We also add the `data-lustre-app`
+attribute to the element we want to mount the app to. You could use a class or an
+id instead, or none of that: [`lustre.start`](/api/lustre#start) takes a CSS
+selector so go wild!
+
+### Hello, world!
+
+Go ahead and rename the generated `lustre_quickstart.gleam` file to `app.gleam`
+and replace the contents with the following:
+
+```gleam
+import lustre
+import lustre/element.{text}
+
+pub fn main() {
+ let app = lustre.element(text("Hello, world!"))
+ let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil)
+
+ Nil
+}
+```
+
+This will create a static Lustre app and mount it onto the element that matches
+the CSS selector. While we're asserting everything is OK here, it is possible
+for `lustre.start` to fail in a couple of ways. Check out the docs for the
+[`lustre.Error`](/api/lustre#error-type) type if you want to know more.
+
+Run `gleam build` and serve the HTML with your preferred static file server (this
+step is necessary: JavaScript modules can't be imported when just opening a HTML
+file) and admire your handiwork.
+
+### Adding interactivity
+
+Now that we know how to get things up and running, let's try something a little
+more exciting and add some interactivity. Replace the contents of your `app.gleam`
+file with the code below and rebuild the project.
+
+```gleam
+import gleam/int
+import lustre
+import lustre/element.{text}
+import lustre/element/html.{div, button, p}
+import lustre/event.{on_click}
+
+pub fn main() {
+ let app = lustre.simple(init, update, view)
+ let assert Ok(_) = lustre.start("[data-lustre-app]", Nil)
+
+ Nil
+}
+
+fn init(_) {
+ 0
+}
+
+type Msg {
+ Incr
+ Decr
+}
+
+fn update(model, msg) {
+ case msg {
+ Incr -> model + 1
+ Decr -> model - 1
+ }
+}
+
+fn view(model) {
+ let count = int.to_string(model)
+
+ div([], [
+ button([on_click(Decr)], [text(" + ")]),
+ p([], [text(count)]),
+ button([on_click(Incr)], [text(" - ")])
+ ])
+}
+```
+
+You should know have a very exciting counter app! Almost every Lustre app will
+boil down to the same three parts:
+
+- A `Model` type that represents your application's state and a function to
+ `init` it.
+- A `Msg` type and an `update` function to update that state based on incoming
+ messages.
+- A `view` function that takes the current state and renders some HTML.
+
+This architecture is not unique to Lustre. It was introduced by the Elm community
+and known as the [Elm Architecture](https://guide.elm-lang.org/architecture/)
+before making its way to React as [Redux](https://redux.js.org) and beyond, known
+more generally as the Model-View-Update architecture. If you work through the
+rest of our guides you'll see how this architecture helps keep side effects out
+of our view code and how to create components that can encapsulate their own state
+and update logic.
+
+For now though, we'll leave things here. If you're interested in seeing how Lustre
+can be used to render static HTML on the server, read on! Otherwise, you can take
+this counter application as a base and start building something of your own.
+"
+
+## On the server | erlang javascript
+
+As we've seen, Lustre is primarily meant to be used in the browser to build
+interactive SPAs. It is possible to render Lustre elements to static HTML and
+simply use Lustre as a templating DSL. As before, we'll start by scaffolding a
+new Gleam project and adding Lustre as a dependency:
+
+```shell
+$ gleam new lustre_quickstart && gleam add lustre
+```
+
+The [`lustre/element`](/api/lustre/element) module contains functions to render
+an element as either a `String` or `StringBuilder`. Copy the following code into
+`lustre_quickstart.gleam`:
+
+```gleam
+import gleam/io
+import lustre/attribute.{attribute}
+import lustre/element.{text}
+import lustre/element/html.{html, head, title, body, div, h1}
+
+pub fn main() {
+ html([attribute("lang", "en")], [
+ head([], [
+ title([], [text("Lustre Quickstart")])
+ ]),
+ body([], [
+ h1([], [text("Hello, world!")])
+ ])
+ ])
+ |> element.to_string
+ |> io.println
+}
+```
+
+We can test this out by running `gleam run` and seeing the HTML printed to the
+console. From here we could set up a web server using [Mist](/guides/mist) or
+[Wisp](/guides/wisp) to serve the HTML to the browser or write it to a file using
+[simplifile](https://hexdocs.pm/simplifile/). Because the API is the same for
+both client and server rendering, it is easy to create reusable components that
+can be rendered anywhere Gleam can run!
+
+### An example with Wisp
+
+Before we go, let's just take a quick look at what it would look like to use
+Lustre in a [Wisp](https://hexdocs.pm/wisp) application. We won't scaffold out a
+real app in this example, but we'll adapt one of the examples from Wisp's own
+documentation.
+
+Specifically, we'll take a look at the `show_form` function from the
+["working with form data"](https://github.com/lpil/wisp/blob/ea8a40bc20745f172695c8cc2dc0a63769f890a7/examples/2-working-with-form-data/src/app/router.gleam#L20)
+example:
+
+```gleam
+...
+
+pub fn show_form() -> Response {
+ // In a larger application a template library or HTML form library might
+ // be used here instead of a string literal.
+ let html =
+ string_builder.from_string(
+ "<form method='post'>
+ <label>Title:
+ <input type='text' name='title'>
+ </label>
+ <label>Name:
+ <input type='text' name='name'>
+ </label>
+ <input type='submit' value='Submit'>
+ </form>",
+ )
+ wisp.ok()
+ |> wisp.html_body(html)
+}
+```
+
+They've helpfully left a comment telling us that in a larger application we might
+want to use a template library, and Lustre is up to the task! Let's refactor this
+using Lustre:
+
+```gleam
+import gleam/string
+import lustre/attribute.{attribute}
+import lustre/element
+import lustre/element/html
+...
+
+pub fn show_form() -> Response {
+ html.form([attribute("method", "post")], [
+ labelled_input("Title"),
+ labelled_input("Name"),
+ html.input([attribute("type", "submit"), attribute("value", "Submit")])
+ ])
+ |> element.to_string_builder
+ |> wisp.html_body
+ |> wisp.ok
+}
+
+fn labelled_input(name: String) -> Element(Nil) {
+ html.label([], [
+ element.text(name <> ": "),
+ html.input([
+ attribute("type", "text"),
+ attribute("name", string.lowercase(name))
+ ])
+ ])
+}
+```
diff --git a/docs/public/page/docs/server-side-rendering.md b/docs/public/page/docs/server-side-rendering.md
new file mode 100644
index 0000000..66d605c
--- /dev/null
+++ b/docs/public/page/docs/server-side-rendering.md
@@ -0,0 +1,8 @@
+# Server-side rendering
+
+Whoopsie, I haven't got round to writing this guide yet. If you haven't checked
+out the [quickstart guide](/docs/quickstart) that is probably the best place to
+go to get up to speed.
+
+If you have any questions, feel free to ping `@hayleigh.dev` over on the Gleam
+[Discord server](https://discord.gg/Fm8Pwmy) and I'd be happy to help you out!
diff --git a/docs/public/page/docs/side-effects.md b/docs/public/page/docs/side-effects.md
new file mode 100644
index 0000000..1cf21e2
--- /dev/null
+++ b/docs/public/page/docs/side-effects.md
@@ -0,0 +1,8 @@
+# Side effects
+
+Whoopsie, I haven't got round to writing this guide yet. If you haven't checked
+out the [quickstart guide](/docs/quickstart) that is probably the best place to
+go to get up to speed.
+
+If you have any questions, feel free to ping `@hayleigh.dev` over on the Gleam
+[Discord server](https://discord.gg/Fm8Pwmy) and I'd be happy to help you out!
diff --git a/docs/public/page/guides/mist.md b/docs/public/page/guides/mist.md
new file mode 100644
index 0000000..9afa83b
--- /dev/null
+++ b/docs/public/page/guides/mist.md
@@ -0,0 +1,8 @@
+# Using with Mist
+
+Whoopsie, I haven't got round to writing this guide yet. If you haven't checked
+out the [quickstart guide](/docs/quickstart) that is probably the best place to
+go to get up to speed.
+
+If you have any questions, feel free to ping `@hayleigh.dev` over on the Gleam
+[Discord server](https://discord.gg/Fm8Pwmy) and I'd be happy to help you out!
diff --git a/docs/public/page/guides/wisp.md b/docs/public/page/guides/wisp.md
new file mode 100644
index 0000000..84d4089
--- /dev/null
+++ b/docs/public/page/guides/wisp.md
@@ -0,0 +1,8 @@
+# Using with Wisp
+
+Whoopsie, I haven't got round to writing this guide yet. If you haven't checked
+out the [quickstart guide](/docs/quickstart) that is probably the best place to
+go to get up to speed.
+
+If you have any questions, feel free to ping `@hayleigh.dev` over on the Gleam
+[Discord server](https://discord.gg/Fm8Pwmy) and I'd be happy to help you out!