diff --git a/examples/sapui5-mill/.gitignore b/examples/sapui5-mill/.gitignore
new file mode 100644
index 0000000..1881b1f
--- /dev/null
+++ b/examples/sapui5-mill/.gitignore
@@ -0,0 +1,36 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+.bsp
+.metals
+.scala-build
+scalajs-modules
+scalawind.scala
+bun.lockb
+yarn.lock
+package-lock.json
+pnpm-lock.yaml
+target
+.bloop
+.idea
\ No newline at end of file
diff --git a/examples/sapui5-mill/.mill-version b/examples/sapui5-mill/.mill-version
new file mode 100644
index 0000000..d61567c
--- /dev/null
+++ b/examples/sapui5-mill/.mill-version
@@ -0,0 +1 @@
+0.12.3
\ No newline at end of file
diff --git a/examples/sapui5-mill/.scalafmt.conf b/examples/sapui5-mill/.scalafmt.conf
new file mode 100644
index 0000000..3ea0470
--- /dev/null
+++ b/examples/sapui5-mill/.scalafmt.conf
@@ -0,0 +1,3 @@
+version = "3.7.15"
+runner.dialect = scala3
+maxColumn=120
\ No newline at end of file
diff --git a/examples/sapui5-mill/README.md b/examples/sapui5-mill/README.md
new file mode 100644
index 0000000..6fead2b
--- /dev/null
+++ b/examples/sapui5-mill/README.md
@@ -0,0 +1,43 @@
+# Scalawind Vite
+
+## Install NodeJS
+
+In case you don't have NodeJS yet, this is my recommendation: https://github.com/Schniz/fnm
+
+## Install dependencies
+
+```bash
+$ npm install
+# or
+$ bun install
+# or
+$ pnpm install
+# or
+$ yarn install
+```
+
+## Start dev server
+
+```bash
+$ npm run dev:scala
+# or
+$ bun dev:scala
+# or
+$ pnpm dev:scala
+# or
+$ yarn dev:scala
+```
+
+In another terminal:
+
+```bash
+$ npm run dev
+# or
+$ bun dev
+# or
+$ pnpm dev
+# or
+$ yarn dev
+```
+
+Open: http://localhost:5173
\ No newline at end of file
diff --git a/examples/sapui5-mill/build.mill b/examples/sapui5-mill/build.mill
new file mode 100644
index 0000000..712828e
--- /dev/null
+++ b/examples/sapui5-mill/build.mill
@@ -0,0 +1,15 @@
+import mill._, scalalib._, scalajslib._, scalajslib.api._
+
+object myapp extends ScalaJSModule {
+ def scalaVersion = "3.3.3"
+ def scalaJSVersion = "1.17.0"
+
+ def moduleKind = ModuleKind.ESModule
+ def moduleSplitStyle = ModuleSplitStyle.SmallModulesFor(List("myapp"))
+
+ def ivyDeps = Agg(
+ ivy"org.scala-js::scalajs-dom::2.8.0",
+ ivy"com.raquo::laminar::17.1.0",
+ ivy"be.doeraene::web-components-ui5::2.0.0",
+ )
+}
diff --git a/examples/sapui5-mill/index.html b/examples/sapui5-mill/index.html
new file mode 100644
index 0000000..e4d32f6
--- /dev/null
+++ b/examples/sapui5-mill/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Scala ❤️ Tailwind
+
+
+
+
+
+
diff --git a/examples/sapui5-mill/main.js b/examples/sapui5-mill/main.js
new file mode 100644
index 0000000..d5b7fff
--- /dev/null
+++ b/examples/sapui5-mill/main.js
@@ -0,0 +1,2 @@
+import './style.css'
+import 'scalajs:main.js'
\ No newline at end of file
diff --git a/examples/sapui5-mill/millw b/examples/sapui5-mill/millw
new file mode 100755
index 0000000..9d343ff
--- /dev/null
+++ b/examples/sapui5-mill/millw
@@ -0,0 +1,241 @@
+#!/usr/bin/env sh
+
+# This is a wrapper script, that automatically download mill from GitHub release pages
+# You can give the required mill version with --mill-version parameter
+# If no version is given, it falls back to the value of DEFAULT_MILL_VERSION
+#
+# Project page: https://github.com/lefou/millw
+# Script Version: 0.4.11
+#
+# If you want to improve this script, please also contribute your changes back!
+#
+# Licensed under the Apache License, Version 2.0
+
+set -e
+
+if [ -z "${DEFAULT_MILL_VERSION}" ] ; then
+ DEFAULT_MILL_VERSION="0.11.4"
+fi
+
+
+if [ -z "${GITHUB_RELEASE_CDN}" ] ; then
+ GITHUB_RELEASE_CDN=""
+fi
+
+
+MILL_REPO_URL="https://github.com/com-lihaoyi/mill"
+
+if [ -z "${CURL_CMD}" ] ; then
+ CURL_CMD=curl
+fi
+
+# Explicit commandline argument takes precedence over all other methods
+if [ "$1" = "--mill-version" ] ; then
+ shift
+ if [ "x$1" != "x" ] ; then
+ MILL_VERSION="$1"
+ shift
+ else
+ echo "You specified --mill-version without a version." 1>&2
+ echo "Please provide a version that matches one provided on" 1>&2
+ echo "${MILL_REPO_URL}/releases" 1>&2
+ false
+ fi
+fi
+
+# Please note, that if a MILL_VERSION is already set in the environment,
+# We reuse it's value and skip searching for a value.
+
+# If not already set, read .mill-version file
+if [ -z "${MILL_VERSION}" ] ; then
+ if [ -f ".mill-version" ] ; then
+ MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)"
+ elif [ -f ".config/mill-version" ] ; then
+ MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)"
+ fi
+fi
+
+MILL_USER_CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/mill"
+
+if [ -z "${MILL_DOWNLOAD_PATH}" ] ; then
+ MILL_DOWNLOAD_PATH="${MILL_USER_CACHE_DIR}/download"
+fi
+
+# If not already set, try to fetch newest from Github
+if [ -z "${MILL_VERSION}" ] ; then
+ # TODO: try to load latest version from release page
+ echo "No mill version specified." 1>&2
+ echo "You should provide a version via '.mill-version' file or --mill-version option." 1>&2
+
+ mkdir -p "${MILL_DOWNLOAD_PATH}"
+ LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 2>/dev/null || (
+ # we might be on OSX or BSD which don't have -d option for touch
+ # but probably a -A [-][[hh]mm]SS
+ touch "${MILL_DOWNLOAD_PATH}/.expire_latest"; touch -A -010000 "${MILL_DOWNLOAD_PATH}/.expire_latest"
+ ) || (
+ # in case we still failed, we retry the first touch command with the intention
+ # to show the (previously suppressed) error message
+ LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest"
+ )
+
+ # POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993
+ # if [ "${MILL_DOWNLOAD_PATH}/.latest" -nt "${MILL_DOWNLOAD_PATH}/.expire_latest" ] ; then
+ if [ -n "$(find -L "${MILL_DOWNLOAD_PATH}/.latest" -prune -newer "${MILL_DOWNLOAD_PATH}/.expire_latest")" ]; then
+ # we know a current latest version
+ MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null)
+ fi
+
+ if [ -z "${MILL_VERSION}" ] ; then
+ # we don't know a current latest version
+ echo "Retrieving latest mill version ..." 1>&2
+ LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null | grep --ignore-case Location: | sed s'/^.*tag\///' | tr -d '\r\n' > "${MILL_DOWNLOAD_PATH}/.latest"
+ MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null)
+ fi
+
+ if [ -z "${MILL_VERSION}" ] ; then
+ # Last resort
+ MILL_VERSION="${DEFAULT_MILL_VERSION}"
+ echo "Falling back to hardcoded mill version ${MILL_VERSION}" 1>&2
+ else
+ echo "Using mill version ${MILL_VERSION}" 1>&2
+ fi
+fi
+
+MILL="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}"
+
+try_to_use_system_mill() {
+ if [ "$(uname)" != "Linux" ]; then
+ return 0
+ fi
+
+ MILL_IN_PATH="$(command -v mill || true)"
+
+ if [ -z "${MILL_IN_PATH}" ]; then
+ return 0
+ fi
+
+ SYSTEM_MILL_FIRST_TWO_BYTES=$(head --bytes=2 "${MILL_IN_PATH}")
+ if [ "${SYSTEM_MILL_FIRST_TWO_BYTES}" = "#!" ]; then
+ # MILL_IN_PATH is (very likely) a shell script and not the mill
+ # executable, ignore it.
+ return 0
+ fi
+
+ SYSTEM_MILL_PATH=$(readlink -e "${MILL_IN_PATH}")
+ SYSTEM_MILL_SIZE=$(stat --format=%s "${SYSTEM_MILL_PATH}")
+ SYSTEM_MILL_MTIME=$(stat --format=%y "${SYSTEM_MILL_PATH}")
+
+ if [ ! -d "${MILL_USER_CACHE_DIR}" ]; then
+ mkdir -p "${MILL_USER_CACHE_DIR}"
+ fi
+
+ SYSTEM_MILL_INFO_FILE="${MILL_USER_CACHE_DIR}/system-mill-info"
+ if [ -f "${SYSTEM_MILL_INFO_FILE}" ]; then
+ parseSystemMillInfo() {
+ LINE_NUMBER="${1}"
+ # Select the line number of the SYSTEM_MILL_INFO_FILE, cut the
+ # variable definition in that line in two halves and return
+ # the value, and finally remove the quotes.
+ sed -n "${LINE_NUMBER}p" "${SYSTEM_MILL_INFO_FILE}" |\
+ cut -d= -f2 |\
+ sed 's/"\(.*\)"/\1/'
+ }
+
+ CACHED_SYSTEM_MILL_PATH=$(parseSystemMillInfo 1)
+ CACHED_SYSTEM_MILL_VERSION=$(parseSystemMillInfo 2)
+ CACHED_SYSTEM_MILL_SIZE=$(parseSystemMillInfo 3)
+ CACHED_SYSTEM_MILL_MTIME=$(parseSystemMillInfo 4)
+
+ if [ "${SYSTEM_MILL_PATH}" = "${CACHED_SYSTEM_MILL_PATH}" ] \
+ && [ "${SYSTEM_MILL_SIZE}" = "${CACHED_SYSTEM_MILL_SIZE}" ] \
+ && [ "${SYSTEM_MILL_MTIME}" = "${CACHED_SYSTEM_MILL_MTIME}" ]; then
+ if [ "${CACHED_SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then
+ MILL="${SYSTEM_MILL_PATH}"
+ return 0
+ else
+ return 0
+ fi
+ fi
+ fi
+
+ SYSTEM_MILL_VERSION=$(${SYSTEM_MILL_PATH} --version | head -n1 | sed -n 's/^Mill.*version \(.*\)/\1/p')
+
+ cat < "${SYSTEM_MILL_INFO_FILE}"
+CACHED_SYSTEM_MILL_PATH="${SYSTEM_MILL_PATH}"
+CACHED_SYSTEM_MILL_VERSION="${SYSTEM_MILL_VERSION}"
+CACHED_SYSTEM_MILL_SIZE="${SYSTEM_MILL_SIZE}"
+CACHED_SYSTEM_MILL_MTIME="${SYSTEM_MILL_MTIME}"
+EOF
+
+ if [ "${SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then
+ MILL="${SYSTEM_MILL_PATH}"
+ fi
+}
+try_to_use_system_mill
+
+# If not already downloaded, download it
+if [ ! -s "${MILL}" ] ; then
+
+ # support old non-XDG download dir
+ MILL_OLD_DOWNLOAD_PATH="${HOME}/.mill/download"
+ OLD_MILL="${MILL_OLD_DOWNLOAD_PATH}/${MILL_VERSION}"
+ if [ -x "${OLD_MILL}" ] ; then
+ MILL="${OLD_MILL}"
+ else
+ case $MILL_VERSION in
+ 0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.* )
+ DOWNLOAD_SUFFIX=""
+ DOWNLOAD_FROM_MAVEN=0
+ ;;
+ 0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M* )
+ DOWNLOAD_SUFFIX="-assembly"
+ DOWNLOAD_FROM_MAVEN=0
+ ;;
+ *)
+ DOWNLOAD_SUFFIX="-assembly"
+ DOWNLOAD_FROM_MAVEN=1
+ ;;
+ esac
+
+ DOWNLOAD_FILE=$(mktemp mill.XXXXXX)
+
+ if [ "$DOWNLOAD_FROM_MAVEN" = "1" ] ; then
+ DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/${MILL_VERSION}/mill-dist-${MILL_VERSION}.jar"
+ else
+ MILL_VERSION_TAG=$(echo "$MILL_VERSION" | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/')
+ DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}"
+ unset MILL_VERSION_TAG
+ fi
+
+ # TODO: handle command not found
+ echo "Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ..." 1>&2
+ ${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${DOWNLOAD_URL}"
+ chmod +x "${DOWNLOAD_FILE}"
+ mkdir -p "${MILL_DOWNLOAD_PATH}"
+ mv "${DOWNLOAD_FILE}" "${MILL}"
+
+ unset DOWNLOAD_FILE
+ unset DOWNLOAD_SUFFIX
+ fi
+fi
+
+if [ -z "$MILL_MAIN_CLI" ] ; then
+ MILL_MAIN_CLI="${0}"
+fi
+
+MILL_FIRST_ARG=""
+if [ "$1" = "--bsp" ] || [ "$1" = "-i" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then
+ # Need to preserve the first position of those listed options
+ MILL_FIRST_ARG=$1
+ shift
+fi
+
+unset MILL_DOWNLOAD_PATH
+unset MILL_OLD_DOWNLOAD_PATH
+unset OLD_MILL
+unset MILL_VERSION
+unset MILL_REPO_URL
+
+# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes
+# shellcheck disable=SC2086
+exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@"
diff --git a/examples/sapui5-mill/myapp/src/myapp/helper.scala b/examples/sapui5-mill/myapp/src/myapp/helper.scala
new file mode 100644
index 0000000..16b2e1b
--- /dev/null
+++ b/examples/sapui5-mill/myapp/src/myapp/helper.scala
@@ -0,0 +1,43 @@
+package myapp
+
+import com.raquo.laminar.api.L.*
+import be.doeraene.webcomponents.ui5.*
+import be.doeraene.webcomponents.ui5.theming.Theming
+
+object ThemeSelector {
+
+ /** Allows to select the theme for the web-components.
+ *
+ * That does not take care of the "rest" of the ui (for example, the general background colour, or text colours...)
+ *
+ * You would need to use the `selectedChoiceVar` current value and adjust remaining css when relevant.
+ */
+ def apply(): HtmlElement = {
+
+ val themeChoices = Vector(
+ "sap_fiori_3",
+ "sap_fiori_3_dark"
+ )
+
+ val selectedChoiceVar = Var(themeChoices(0))
+
+ Select(
+ themeChoices.map { theme =>
+ Select.option(
+ theme,
+ _.value := theme,
+ _.selected <-- selectedChoiceVar.signal.map(_ == theme)
+ )
+ },
+ _.events.onChange.map(_.detail.selectedOption.maybeValue.getOrElse(themeChoices(0))) --> selectedChoiceVar.writer,
+ selectedChoiceVar.signal.changes --> Observer(Theming.setTheme)
+ )
+
+ }
+
+ // registering the themes
+ Theming.WebComponentsAssets
+ Theming.WebComponentsFioriAssets
+ Theming.WebComponentsCompatAssets
+
+}
diff --git a/examples/sapui5-mill/myapp/src/myapp/index.scala b/examples/sapui5-mill/myapp/src/myapp/index.scala
new file mode 100644
index 0000000..6d20505
--- /dev/null
+++ b/examples/sapui5-mill/myapp/src/myapp/index.scala
@@ -0,0 +1,19 @@
+package myapp
+
+import org.scalajs.dom
+
+import com.raquo.laminar.api.L.*
+import be.doeraene.webcomponents.ui5.*
+import be.doeraene.webcomponents.ui5.configkeys.*
+
+@main
+def main(): Unit = {
+ val container = dom.document.getElementById("app")
+ render(
+ container,
+ div(
+ ThemeSelector(),
+ Button(_.design := ButtonDesign.Default, "Default")
+ )
+ )
+}
diff --git a/examples/sapui5-mill/package.json b/examples/sapui5-mill/package.json
new file mode 100644
index 0000000..da906b0
--- /dev/null
+++ b/examples/sapui5-mill/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "scalawind-vite-mill",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview",
+ "dev:scala": "./millw -w myapp.fastLinkJS",
+ "build:scala": "./millw myapp.fullLinkJS"
+ },
+ "devDependencies": {
+ "autoprefixer": "10.4.19",
+ "postcss": "8.4.38",
+ "tailwindcss": "3.4.3",
+ "vite": "6.0.1"
+ },
+ "dependencies": {
+ "@ui5/webcomponents": "2.4.0",
+ "@ui5/webcomponents-compat": "2.4.0",
+ "@ui5/webcomponents-fiori": "2.4.0",
+ "@ui5/webcomponents-icons": "2.4.0"
+ }
+}
diff --git a/examples/sapui5-mill/postcss.config.js b/examples/sapui5-mill/postcss.config.js
new file mode 100644
index 0000000..2e7af2b
--- /dev/null
+++ b/examples/sapui5-mill/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/examples/sapui5-mill/public/vite.svg b/examples/sapui5-mill/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/examples/sapui5-mill/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/sapui5-mill/style.css b/examples/sapui5-mill/style.css
new file mode 100644
index 0000000..7ce3e99
--- /dev/null
+++ b/examples/sapui5-mill/style.css
@@ -0,0 +1,3 @@
+/* @tailwind base;
+@tailwind components;
+@tailwind utilities; */
\ No newline at end of file
diff --git a/examples/sapui5-mill/tailwind.config.cjs b/examples/sapui5-mill/tailwind.config.cjs
new file mode 100644
index 0000000..edfb2da
--- /dev/null
+++ b/examples/sapui5-mill/tailwind.config.cjs
@@ -0,0 +1,10 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: {
+ files: [
+ './index.html',
+ './out/myapp/fastLinkJS.dest/**/*.js',
+ './out/myapp/fullLinkJS.dest/**/*.js'
+ ],
+ },
+};
\ No newline at end of file
diff --git a/examples/sapui5-mill/vite.config.js b/examples/sapui5-mill/vite.config.js
new file mode 100644
index 0000000..d042e6d
--- /dev/null
+++ b/examples/sapui5-mill/vite.config.js
@@ -0,0 +1,14 @@
+import { defineConfig } from 'vite';
+
+const isProd = process.env.NODE_ENV == "production";
+
+export default defineConfig({
+ resolve: {
+ alias: [
+ {
+ find: /^scalajs:(.*)$/,
+ replacement: `/out/myapp/${isProd ? 'full' : 'fast'}LinkJS.dest/$1`
+ }
+ ]
+ }
+});
\ No newline at end of file