Tyrian, an Elm inspired frontend framework for Scala.js.

Rendering HTML

HTML syntax

In Tyrian, you describe your view in Scala and the VirtualDom implementation that powers Tyrian (Scala.js Snabbdom) renders that description into HTML.

Here is a simple made up example of the syntax to give you a flavor:

import tyrian.*
import tyrian.Html.*
import tyrian.CSS

enum Msg:
  case Greet

val myStyles  = style(CSS.`font-family`("Arial, Helvetica, sans-serif"))
val topLine = p(b(text("This is some HTML in bold.")))

div(id := "my-container")(
  div(myStyles)(
    topLine,
    p("Hello, world!"),
    button(onClick(Msg.Greet))("Say hello!")
  )
)

Working with Tags, Attributes and Properties

A vast list of the common HTML tags, attributes, and CSS property types have been generated for you, and effort has gone in to making the experience reasonably pleasant.

The arrangement of tags follows similar approaches by other compiled languages trying to represent HTML:

tag-name(List(attributes, ...))(List(children, ...))

For example, here we have a span with an attribute and a child element:

span(`class` := "green-box")(
  p("This is some text.")
)

You can omit the attributes and the syntax is valid:

span(
  p("This is some text.")
)

Note that plain text can be declare as text or just omitted, in other words these are equivalent:

p("some text")
p(text("some text"))

To distinguish them from similarly named tags (e.g. the title attribute and the title tag...), attributes are declared as attribute-name := attribute-value, e.g.:

id := "my-container"

Some HTML attributes / properties use Scala reserved words, and so have a variety of encodings to suit all tastes. For example class is a Scala reserved word and you cannot use it directly, but you can use any of these instead:

`class`
cls
className
_class

Styles are also baked in, albeit it in a slightly crude way, but you will get some IDE support. You can do things like the following:

p(style(CSS.`font-weight`("bold")))("Hello")

Optional tags

Sometime you might want to optionally render a tag, or not. To help with this, you can use the orEmpty extension method:

import tyrian.syntax.*

Option(p("Show this!")).orEmpty

Or the Empty type:

import tyrian.syntax.*

if showIt then p("Show this!") else Empty

SVG

You can also pull in SVG tags and attributes using:

import tyrian.SVG.*

CSS

Many standard CSS terms can be imported using:

import tyrian.CSS.*

Rolling your own

If you find we've missed a tag or attribute or something, please raise an issue. In the meantime, you can always make your own. Here are just a few made up examples, each of these has numerous constructors for you to explore:

// A 'canvas' tag
tag("canvas")(id := "an-id")(Nil)
// or
Tag("canvas", List(id := "an-id"), Nil)

// An attribute
attribute("my-attribute", "its-value")

// A property
property("my-property", "its-value")

// Styles
style("width", "100px")
styles("width" -> "100px", "height" -> "50px")

// An event-type attribute
onEvent("click", (evt: Tyrian.Event) => Msg.Greet)
Note that everything is stringly typed, and that's because in HTML everything is stringly typed too! In the generated tags, we've added support for things like attributes accepting Ints and Booleans and so on where they have known acceptable input types.

Known Gotcha: Attributes vs. Properties

One occasion where you may need to make your own tags or attributes etc., is when Tyrian has an incorrectly declared definition of something or other.

A previous case of this was where the input field value property was incorrectly declared as an attribute, and so the value wasn't changed as expected based on model updates.

Effort has gone in to getting these things right, but if you come across any issues, please report them, and the workaround is to re-declare it yourself as above while a fix is produced.