Scala3でScalaHTML, ScalaCSSを使う

# 背景
Scala 3でWebアプリケーションを作成したい。
その際に、HTML, CSSもScalaを使って書きたい。

## 解決策
Scala 3 で ScalaHTML, ScalaCSSを使う。

ScalaHTML
https://index.scala-lang.org/yrichika/scalahtml

ScalaCSS
https://japgolly.github.io/scalacss/book/index.html

# 環境
Windows 11
Java: OpenJDK 17.0.7
Scala : 3.3.0
sbt : 1.9.0
Akka: 2.7.0
Akka-HTTP: 10.5.2

※1. Akka, Akka-HTTPについては、ライセンスがBSLなので、無料で使えるのは条件のある環境になります。Scala 3がサポートされているAkka-HTTPを使おうとすると、Akkaは2.7以上を使わなければならないので、必然的にBSLになります。

※2. これから導入するScalaHTMLの要件は`3.0 >= Scala >= 2.13.5`ですが、上のAkka-HTTPが3.0.0で動かないので、Scalaのバージョンを3.3.0にしています。一応、コンパイルは通ります。

# 導入
`libraryDependencies`を更新する

```
libraryDependencies += "io.github.yrichika" %% "scalahtml" % "0.1.1"
libraryDependencies += "com.github.japgolly.scalacss" %% "core" % "0.8.0-RC1"
```

適宜、`Seq()`などを使って書き換えます。

## ソースコードを作成する

適宜、プロジェクトの適切な位置にソースコードを置きましょう。
ScalaHTML
``` HTML.scala
package com.isageek.hakushimeita.xnote.client

import scalahtml.Tags._

object MyHtml {
    val html = html5() {
        "any string or tag methods here"
    }

    def render() = html 
}
```

ScalaCSS 
``` Style.scala
package com.isageek.hakushimeita.xnote.client

import scala.language.postfixOps
import scalacss.DevDefaults._

object MyStyles extends StyleSheet.Standalone {
  import dsl._

  "div.std" - (
    margin(12 px, auto),
    textAlign.left,
    cursor.pointer,

    &.hover -
      cursor.zoomIn,

    media.not.handheld.landscape.maxWidth(640 px) -
      width(400 px),

    &("span") -
      color.red
  )

  "h1".firstChild -
    fontWeight.bold

  for (i <- 0 to 3)
    s".indent-$i" -
      paddingLeft(i * 2.ex)
}
```
※ オフィシャルなドキュメントには載っていませんが、`import scala.language.postfixOps`が必要

Akka-HTTP
HTMLファイルを読み込むのではなくて、
先ほど作成した`MyHTML`オブジェクトから、直接取得します。
``` Main.scala
/*
@main def hello: Unit =
  println("Hello world!")
  println(msg)

def msg = "I was compiled by Scala 3. :)"
*/

/*
 * Copyright (C) 2020-2023 Lightbend Inc. <https://www.lightbend.com>
 */

//package docs.http.scaladsl
package $package$.server

import akka.actor.typed.ActorSystem
import akka.actor.typed.scaladsl.Behaviors
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import scala.io.StdIn

import $package$.client.MyHtml

object HttpServerRoutingMinimal {

  def main(args: Array[String]): Unit = {

    implicit val system = ActorSystem(Behaviors.empty, "my-system")
    // needed for the future flatMap/onComplete in the end
    implicit val executionContext = system.executionContext

    val route =
      path("hello") {
        get {
//          complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
          complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, MyHtml.render()))
        }
      }

    val bindingFuture = Http().newServerAt("localhost", 8080).bind(route)

    println(s"Server now online. Please navigate to http://localhost:8080/hello\nPress RETURN to stop...")
    StdIn.readLine() // let it run until user presses return
    bindingFuture
      .flatMap(_.unbind()) // trigger unbinding from the port
      .onComplete(_ => system.terminate()) // and shutdown when done
  }
}
```

# 実行
`sbt run` するだけ。
built.sbtの設定が不十分なのか、warningっぽいのが出るが、動くので今回は一応無視します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477945/db5dd0d5-62ea-be1d-23f0-7a2707200289.png)

出力されました。

# 予定
Scala.jsを使うので、サイトの構成はSPAにしたい。
フレームワークLaminarを導入したいところ。
なので、ScalaHTMLは導入しましたが、たくさん使うわけではありません。
index.htmlになるものを作って、お役目を果たします。
Akka-HTTPは今回の趣旨にあっていて、便利なのだけれど、ライセンスの関係で、プロダクトには開発ライセンスが発生するため、おそらく変えなければならない。いい代わりが見つかるといいですが…