diff --git a/examples/e-commerce-product-page/src/style.css b/examples/e-commerce-product-page/src/style.css
index 2ca615e4..99573ed4 100644
--- a/examples/e-commerce-product-page/src/style.css
+++ b/examples/e-commerce-product-page/src/style.css
@@ -20,10 +20,6 @@
--color-gray-50: hsl(0, 0%, 95%);
}
-html[data-theme="dark"] {
- --background-color: #202c37;
-}
-
body {
font-family: "Kumbh Sans", sans-serif;
max-width: 100vw;
diff --git a/examples/multi-step-form/.gitignore b/examples/multi-step-form/.gitignore
new file mode 100644
index 00000000..a547bf36
--- /dev/null
+++ b/examples/multi-step-form/.gitignore
@@ -0,0 +1,24 @@
+# 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?
diff --git a/examples/multi-step-form/.npmrc b/examples/multi-step-form/.npmrc
new file mode 100644
index 00000000..6b5f38e8
--- /dev/null
+++ b/examples/multi-step-form/.npmrc
@@ -0,0 +1,2 @@
+save-exact = true
+package-lock = false
diff --git a/examples/multi-step-form/README.md b/examples/multi-step-form/README.md
new file mode 100644
index 00000000..9b0d24ae
--- /dev/null
+++ b/examples/multi-step-form/README.md
@@ -0,0 +1,23 @@
+# Frontend Mentor Multi Step Form
+
+Here is the implementation in [Bau.js](https://github.com/grucloud/bau) of the [Frontend Mentor Multi Step Form code challenge](https://www.frontendmentor.io/challenges/multistep-form-YVAnSdqQBJ)
+
+## Workflow
+
+Install the dependencies:
+
+```sh
+npm install
+```
+
+Start a development server:
+
+```sh
+npm run dev
+```
+
+Build a production version:
+
+```sh
+npm run build
+```
diff --git a/examples/multi-step-form/index.html b/examples/multi-step-form/index.html
new file mode 100644
index 00000000..6d4b66e3
--- /dev/null
+++ b/examples/multi-step-form/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Multi Step Form | FrontendMentor
+
+
+
+
+
+
diff --git a/examples/multi-step-form/package.json b/examples/multi-step-form/package.json
new file mode 100644
index 00000000..7f684b90
--- /dev/null
+++ b/examples/multi-step-form/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "frontendmentor-multi-step-form",
+ "private": true,
+ "version": "0.90.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "typescript": "^5.0.2",
+ "vite": "^5.2.11"
+ },
+ "dependencies": {
+ "@grucloud/bau": "^0.90.0",
+ "@grucloud/bau-css": "^0.90.0",
+ "@grucloud/bau-ui": "^0.90.0"
+ }
+}
diff --git a/examples/multi-step-form/public/assets/images/bg-sidebar-desktop.svg b/examples/multi-step-form/public/assets/images/bg-sidebar-desktop.svg
new file mode 100644
index 00000000..d9153d9f
--- /dev/null
+++ b/examples/multi-step-form/public/assets/images/bg-sidebar-desktop.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/multi-step-form/public/assets/images/bg-sidebar-mobile.svg b/examples/multi-step-form/public/assets/images/bg-sidebar-mobile.svg
new file mode 100644
index 00000000..0097d56a
--- /dev/null
+++ b/examples/multi-step-form/public/assets/images/bg-sidebar-mobile.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/multi-step-form/public/assets/images/favicon-32x32.png b/examples/multi-step-form/public/assets/images/favicon-32x32.png
new file mode 100644
index 00000000..1e2df7f0
Binary files /dev/null and b/examples/multi-step-form/public/assets/images/favicon-32x32.png differ
diff --git a/examples/multi-step-form/public/assets/images/icon-advanced.svg b/examples/multi-step-form/public/assets/images/icon-advanced.svg
new file mode 100644
index 00000000..a0b2c3f8
--- /dev/null
+++ b/examples/multi-step-form/public/assets/images/icon-advanced.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/multi-step-form/public/assets/images/icon-arcade.svg b/examples/multi-step-form/public/assets/images/icon-arcade.svg
new file mode 100644
index 00000000..5e062fe0
--- /dev/null
+++ b/examples/multi-step-form/public/assets/images/icon-arcade.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/multi-step-form/public/assets/images/icon-checkmark.svg b/examples/multi-step-form/public/assets/images/icon-checkmark.svg
new file mode 100644
index 00000000..f6017b21
--- /dev/null
+++ b/examples/multi-step-form/public/assets/images/icon-checkmark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/multi-step-form/public/assets/images/icon-pro.svg b/examples/multi-step-form/public/assets/images/icon-pro.svg
new file mode 100644
index 00000000..57e438ac
--- /dev/null
+++ b/examples/multi-step-form/public/assets/images/icon-pro.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/multi-step-form/public/assets/images/icon-thank-you.svg b/examples/multi-step-form/public/assets/images/icon-thank-you.svg
new file mode 100644
index 00000000..5aea55de
--- /dev/null
+++ b/examples/multi-step-form/public/assets/images/icon-thank-you.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/multi-step-form/src/main.ts b/examples/multi-step-form/src/main.ts
new file mode 100644
index 00000000..a7a5a755
--- /dev/null
+++ b/examples/multi-step-form/src/main.ts
@@ -0,0 +1,20 @@
+import { createContext, type Context } from "@grucloud/bau-ui/context";
+import multiStepForm from "./multiStepForm";
+
+import "./style.css";
+
+const context = createContext();
+
+const app = (context: Context) => {
+ const { bau } = context;
+ const { main } = bau.tags;
+
+ const MultiStepForm = multiStepForm(context);
+
+ return function () {
+ return main(MultiStepForm());
+ };
+};
+
+const App = app(context);
+document.getElementById("app")?.replaceChildren(App());
diff --git a/examples/multi-step-form/src/multiStepForm.ts b/examples/multi-step-form/src/multiStepForm.ts
new file mode 100644
index 00000000..41944135
--- /dev/null
+++ b/examples/multi-step-form/src/multiStepForm.ts
@@ -0,0 +1,140 @@
+import { type Context } from "@grucloud/bau-ui/context";
+import stepYourInfo from "./stepYourInfo";
+import stepSelectPlan from "./stepSelectPlan";
+
+export default function (context: Context) {
+ const { bau, css } = context;
+ const { article, div, header, ul, li, span } = bau.tags;
+ const StepYourInfo = stepYourInfo(context);
+ const StepSelectPlan = stepSelectPlan(context);
+
+ const currentStepState = bau.state(1);
+
+ const className = css`
+ border: 1px solid red;
+ max-width: 1000px;
+ margin: auto;
+ display: flex;
+ justify-content: center;
+ align-items: stretch;
+ gap: 0.6rem;
+ border-radius: 0.6rem;
+ padding: 1rem;
+
+ & header {
+ border: 1px solid blue;
+ background-image: url("./assets/images/bg-sidebar-desktop.svg");
+ background-size: cover;
+ > ul {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ padding: 1rem;
+ > li {
+ &.active {
+ color: red;
+ }
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ color: var(--font-color-inverse);
+
+ .step-number {
+ font-weight: bold;
+ padding: 1rem;
+ border: 1px solid white;
+ border-radius: 100%;
+ width: 2rem;
+ height: 2rem;
+ display: grid;
+ place-content: center;
+ }
+ .step-label {
+ text-transform: uppercase;
+ font-size: smaller;
+ color: var(--font-color-inverse-secondary);
+ }
+ .label {
+ text-transform: uppercase;
+ font-weight: bold;
+ }
+ }
+ }
+ }
+ .content {
+ border: 1px solid blue;
+ min-width: 375px;
+ min-height: 400px;
+ > ul {
+ > li {
+ display: none;
+ &.active {
+ display: block;
+ }
+ form {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ }
+ }
+ }
+ }
+ `;
+ const isCurrentIndex = (index: number) => currentStepState.val == index;
+
+ const Header = ({ index, label }: any) =>
+ li(
+ { class: () => isCurrentIndex(index) && "active" },
+ div({ class: "step-number" }, index),
+ div(
+ div({ class: "step-label" }, "Step", index),
+ div({ class: "label" }, label)
+ )
+ );
+
+ const next = () => {
+ currentStepState.val++;
+ };
+ const previous = () => {
+ currentStepState.val--;
+ };
+
+ const onsubmitYourInfo = (event: HTMLFormElement) => {
+ event.preventDefault();
+ const payload = Object.fromEntries(new FormData(event.currentTarget));
+ next();
+ };
+
+ const onsubmitAddon = (event) => {
+ event.preventDefault();
+ const payload = Object.fromEntries(new FormData(event.currentTarget));
+ alert(JSON.stringify(payload));
+ // next();
+ };
+
+ const steps = [
+ {
+ label: "Your Info",
+ Content: () => StepYourInfo({ onsubmit: onsubmitYourInfo }),
+ },
+ {
+ label: "Select Plan",
+ Content: ({}) => StepSelectPlan({ onsubmit: onsubmitAddon }),
+ },
+ ];
+
+ return () => {
+ return article(
+ { class: className },
+ header(ul(steps.map(({ label }, index) => Header({ index, label })))),
+ div(
+ { class: "content" },
+ ul(
+ steps.map(({ Content, label }, index) =>
+ li({ class: () => isCurrentIndex(index) && "active" }, Content({}))
+ )
+ )
+ )
+ );
+ };
+}
diff --git a/examples/multi-step-form/src/stepAddOns.ts b/examples/multi-step-form/src/stepAddOns.ts
new file mode 100644
index 00000000..681e37c5
--- /dev/null
+++ b/examples/multi-step-form/src/stepAddOns.ts
@@ -0,0 +1,18 @@
+import { type Context } from "@grucloud/bau-ui/context";
+
+export default function (context: Context) {
+ const { bau, css } = context;
+ const { form, h1, button } = bau.tags;
+
+ const className = css`
+ border: 1px solid red;
+ `;
+
+ return ({ onsubmit }) => {
+ return form(
+ { class: className, onsubmit },
+ h1("Add-Ons"),
+ button({ type: "submit" }, "Next")
+ );
+ };
+}
diff --git a/examples/multi-step-form/src/stepSelectPlan.ts b/examples/multi-step-form/src/stepSelectPlan.ts
new file mode 100644
index 00000000..c6e6730d
--- /dev/null
+++ b/examples/multi-step-form/src/stepSelectPlan.ts
@@ -0,0 +1,60 @@
+import { type Context } from "@grucloud/bau-ui/context";
+
+export default function (context: Context) {
+ const { bau, css } = context;
+ const {
+ form,
+ button,
+ h1,
+ p,
+ input,
+ footer,
+ div,
+ label,
+ span,
+ strong,
+ small,
+ } = bau.tags;
+
+ const className = css`
+ border: 1px solid red;
+ .radio-group {
+ display: flex;
+ justify-content: space-around;
+ > label {
+ border: 1px solid red;
+ padding: 1rem;
+ }
+ }
+ `;
+
+ return ({ onsubmit }: any) => {
+ return form(
+ { class: className, onsubmit },
+ h1("Select your plan"),
+ p("You have the option of monthly or yearly billing."),
+ div(
+ { class: "radio-group" },
+ label(
+ input({ type: "radio", name: "plan", id: "arcade" }),
+ p(strong("Arcade")),
+ p(small("$9/month"))
+ ),
+ label(
+ input({ type: "radio", name: "plan", id: "advanced" }),
+ p(strong("Advanced")),
+ p(small("$12/month"))
+ ),
+ label(
+ input({ type: "radio", name: "plan", id: "pro" }),
+ p(strong("Pro")),
+ p(small("$15/month"))
+ )
+ ),
+ footer(
+ button({ type: "button" }, "Go back"),
+ button({ type: "submit" }, "Next Step")
+ )
+ );
+ };
+}
diff --git a/examples/multi-step-form/src/stepSummary.ts b/examples/multi-step-form/src/stepSummary.ts
new file mode 100644
index 00000000..f4ed2d2c
--- /dev/null
+++ b/examples/multi-step-form/src/stepSummary.ts
@@ -0,0 +1,14 @@
+import { type Context } from "@grucloud/bau-ui/context";
+
+export default function (context: Context) {
+ const { bau, css } = context;
+ const { section, h1 } = bau.tags;
+
+ const className = css`
+ border: 1px solid red;
+ `;
+
+ return () => {
+ return section({ class: className }, h1("SSummary"));
+ };
+}
diff --git a/examples/multi-step-form/src/stepYourInfo.ts b/examples/multi-step-form/src/stepYourInfo.ts
new file mode 100644
index 00000000..0633e888
--- /dev/null
+++ b/examples/multi-step-form/src/stepYourInfo.ts
@@ -0,0 +1,32 @@
+import { type Context } from "@grucloud/bau-ui/context";
+
+export default function (context: Context) {
+ const { bau, css } = context;
+ const { form, h1, input, label, button } = bau.tags;
+
+ const className = css`
+ border: 1px solid red;
+ display: flex;
+ flex-direction: column;
+ `;
+
+ return ({ onsubmit }) => {
+ return form(
+ { class: className, onsubmit },
+ h1("Your Info"),
+ label(
+ "Name",
+ input({ name: "name", placeholder: "e.g Stephen King", required: true })
+ ),
+ label(
+ "Email Address",
+ input({ type: "email", placeholder: "e.g stephenking@lorem.com" })
+ ),
+ label(
+ "Phone Number",
+ input({ name: "phone", placeholder: "e.g. 1234567890" })
+ ),
+ button({ type: "submit" }, "Next")
+ );
+ };
+}
diff --git a/examples/multi-step-form/src/style.css b/examples/multi-step-form/src/style.css
new file mode 100644
index 00000000..5bdddd1a
--- /dev/null
+++ b/examples/multi-step-form/src/style.css
@@ -0,0 +1,57 @@
+@import url("https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;500;700&display=swap");
+
+* {
+ margin: 0;
+ box-sizing: border-box;
+}
+/**
+- Marine blue: hsl(213, 96%, 18%)
+- Purplish blue: hsl(243, 100%, 62%)
+- Pastel blue: hsl(228, 100%, 84%)
+- Light blue: hsl(206, 94%, 87%)
+- Strawberry red: hsl(354, 84%, 57%)
+
+### Neutral
+
+- Cool gray: hsl(231, 11%, 63%)
+- Light gray: hsl(229, 24%, 87%)
+- Magnolia: hsl(217, 100%, 97%)
+- Alabaster: hsl(231, 100%, 99%)
+- White: hsl(0, 0%, 100%)
+
+*/
+:root {
+ --color-primary-h: 26;
+ --color-primary-base-s: 100%;
+ --color-primary-l: 55%;
+
+ --color-neutral-h: 219;
+ --color-neutral-base-s: 9%;
+ --color-neutral-l: 45%;
+
+ --background-color: #fafafa;
+ --font-size: 1rem;
+ --font-color-secondary: #68707d;
+}
+
+body {
+ font-family: "Ubuntu", sans-serif;
+ max-width: 100vw;
+ background-color: var(--background-color);
+ font-size: var(--font-size);
+}
+main {
+ display: grid;
+ align-items: center;
+ min-height: 100vh;
+}
+
+ul {
+ list-style: none;
+}
+button {
+ cursor: pointer;
+ /* background: none;
+ border: none;
+ border-radius: 1rem; */
+}
diff --git a/examples/multi-step-form/src/vite-env.d.ts b/examples/multi-step-form/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/examples/multi-step-form/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/multi-step-form/tsconfig.json b/examples/multi-step-form/tsconfig.json
new file mode 100644
index 00000000..75abdef2
--- /dev/null
+++ b/examples/multi-step-form/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/examples/multi-step-form/vite.config.js b/examples/multi-step-form/vite.config.js
new file mode 100644
index 00000000..41713bec
--- /dev/null
+++ b/examples/multi-step-form/vite.config.js
@@ -0,0 +1,9 @@
+import { defineConfig } from "vite";
+
+export default defineConfig(({ command, mode, ssrBuild }) => {
+ return {
+ server: {
+ open: true,
+ },
+ };
+});