diff --git a/examples/recipePage/.gitignore b/examples/recipePage/.gitignore
new file mode 100644
index 00000000..a547bf36
--- /dev/null
+++ b/examples/recipePage/.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/recipePage/.npmrc b/examples/recipePage/.npmrc
new file mode 100644
index 00000000..6b5f38e8
--- /dev/null
+++ b/examples/recipePage/.npmrc
@@ -0,0 +1,2 @@
+save-exact = true
+package-lock = false
diff --git a/examples/recipePage/README.md b/examples/recipePage/README.md
new file mode 100644
index 00000000..d551de88
--- /dev/null
+++ b/examples/recipePage/README.md
@@ -0,0 +1,23 @@
+# Frontend Mentor Recipe Page
+
+Here is the implementation in [Bau.js](https://github.com/grucloud/bau) of the [Frontend Mentor Recipe Page code challenge](https://www.frontendmentor.io/challenges/https://www.frontendmentor.io/challenges/recipe-page-KiTsR8QQKm)
+
+## 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/recipePage/index.html b/examples/recipePage/index.html
new file mode 100644
index 00000000..f31b3054
--- /dev/null
+++ b/examples/recipePage/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Recipe Page | FrontendMentor
+
+
+
+
+
+
diff --git a/examples/recipePage/package.json b/examples/recipePage/package.json
new file mode 100644
index 00000000..d2fab061
--- /dev/null
+++ b/examples/recipePage/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "frontendmentor-recipe-page",
+ "private": true,
+ "version": "0.85.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.85.0",
+ "@grucloud/bau-css": "^0.85.0",
+ "@grucloud/bau-ui": "^0.85.0"
+ }
+}
diff --git a/examples/recipePage/public/assets/images/favicon-32x32.png b/examples/recipePage/public/assets/images/favicon-32x32.png
new file mode 100644
index 00000000..1e2df7f0
Binary files /dev/null and b/examples/recipePage/public/assets/images/favicon-32x32.png differ
diff --git a/examples/recipePage/public/assets/images/image-omelette.jpeg b/examples/recipePage/public/assets/images/image-omelette.jpeg
new file mode 100644
index 00000000..dac3a468
Binary files /dev/null and b/examples/recipePage/public/assets/images/image-omelette.jpeg differ
diff --git a/examples/recipePage/src/main.ts b/examples/recipePage/src/main.ts
new file mode 100644
index 00000000..ea28603b
--- /dev/null
+++ b/examples/recipePage/src/main.ts
@@ -0,0 +1,20 @@
+import { createContext, type Context } from "@grucloud/bau-ui/context";
+import recipePage from "./recipePage";
+
+import "./style.css";
+
+const context = createContext();
+
+const app = (context: Context) => {
+ const { bau } = context;
+ const { main } = bau.tags;
+
+ const RecipePage = recipePage(context);
+
+ return function () {
+ return main(RecipePage());
+ };
+};
+
+const App = app(context);
+document.getElementById("app")?.replaceChildren(App());
diff --git a/examples/recipePage/src/recipePage.ts b/examples/recipePage/src/recipePage.ts
new file mode 100644
index 00000000..f76d0c8d
--- /dev/null
+++ b/examples/recipePage/src/recipePage.ts
@@ -0,0 +1,162 @@
+import { type Context } from "@grucloud/bau-ui/context";
+
+export default function (context: Context) {
+ const { bau, css } = context;
+ const {
+ h1,
+ h2,
+ h3,
+ p,
+ article,
+ img,
+ section,
+ strong,
+ ul,
+ ol,
+ li,
+ table,
+ tbody,
+ th,
+ tr,
+ td,
+ } = bau.tags;
+
+ const className = css`
+ max-width: 700px;
+ padding: 1.5rem;
+ margin: 5rem 0;
+ @media (max-width: 600px) {
+ margin: 0;
+ }
+ background-color: var(--White);
+ border-radius: 0.5rem;
+
+ .img-omelete {
+ width: 100%;
+ border-radius: 0.5rem;
+ }
+ h1,
+ h2 {
+ font-family: "Young Serif";
+ }
+
+ h1 {
+ color: var(--Stone-900);
+ font-weight: 400;
+ font-size: 2.2em;
+ }
+ h2 {
+ color: var(--Brown-800);
+ font-weight: 400;
+ }
+ ul,
+ ol {
+ padding-left: 1rem;
+ li {
+ color: var(--WengeBrown);
+ padding: 0.3rem;
+ list-style-position: inside;
+ }
+ }
+ section.preparation-time {
+ background-color: var(--Rose-50);
+ padding: 1.2rem;
+ border-radius: 0.7rem;
+ h3 {
+ color: var(--Rose-800);
+ }
+ }
+ ol {
+ li::marker {
+ font-weight: 700;
+ color: var(--Brown-800);
+ }
+ }
+ table {
+ width: 100%;
+ border-collapse: collapse;
+ tbody {
+ tr {
+ border-bottom: 1px solid var(--Stone-150);
+ th {
+ padding: 1rem;
+ font-weight: normal;
+ text-align: start;
+ color: var(--Stone-600);
+ }
+ td {
+ font-weight: 700;
+ color: var(--Rose-800);
+ }
+ }
+ }
+ }
+ `;
+
+ return function myComponent() {
+ return article(
+ { class: className },
+ img({
+ class: "img-omelete",
+ src: "./assets/images/image-omelette.jpeg",
+ alt: "omelette",
+ }),
+ h1("Simple Omelette Recipe"),
+ p(
+ "An easy and quick dish, perfect for any meal. This classic omelette combines beaten eggs cooked to perfection, optionally filled with your choice of cheese, vegetables, or meats."
+ ),
+ section(
+ { class: "preparation-time" },
+ h3("Preparation Time"),
+ ul(
+ li(strong("Total: "), "Approximately 10 minutes"),
+ li(strong("Preparation: "), "5 minutes"),
+ li(strong("Cooking: "), "5 minutes")
+ )
+ ),
+ h2("Ingredients"),
+ ul(
+ li("2-3 large eggs"),
+ li("Salt, to taste"),
+ li("Pepper, to taste"),
+ li("1 tablespoon of butter or oil"),
+ li("Optional fillings: cheese, diced vegetables, cooked meats, herbs")
+ ),
+ h2("Instructions"),
+ ol(
+ li(
+ strong("Beat the eggs: "),
+ " In a bowl, beat the eggs with a pinch of salt and pepper until they are well mixed. You can add a tablespoon of water or milk for a fluffier texture."
+ ),
+ li(
+ strong("Heat the pan: "),
+ "Place a non-stick frying pan over medium heat and add butter or oil."
+ ),
+ li(
+ strong("Add fillings (optional): "),
+ "When the eggs begin to set at the edges but are still slightly runny in the middle, sprinkle your chosen fillings over one half of the omelette."
+ ),
+ li(
+ strong("Fold and serve: "),
+ "As the omelette continues to cook, carefully lift one edge and fold it over the fillings. Let it cook for another minute, then slide it onto a plate."
+ ),
+ li(
+ strong("Enjoy: "),
+ "Serve hot, with additional salt and pepper if needed."
+ )
+ ),
+ h2("Nutrition"),
+ p(
+ "The table below shows nutritional values per serving without the additional fillings."
+ ),
+ table(
+ tbody(
+ tr(th("Calories"), td("277kcal")),
+ tr(th("Carbs"), td("0g")),
+ tr(th("Protien"), td("20g")),
+ tr(th("Fat"), td("22g"))
+ )
+ )
+ );
+ };
+}
diff --git a/examples/recipePage/src/style.css b/examples/recipePage/src/style.css
new file mode 100644
index 00000000..864ad02b
--- /dev/null
+++ b/examples/recipePage/src/style.css
@@ -0,0 +1,28 @@
+@import url("https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&family=Young+Serif&display=swap");
+
+* {
+ margin: 0;
+ box-sizing: border-box;
+}
+
+:root {
+ --White: hsl(0, 0%, 100%);
+ --Stone-100: hsl(30, 54%, 90%);
+ --Stone-150: hsl(30, 18%, 87%);
+ --Stone-600: hsl(30, 10%, 34%);
+ --Stone-900: hsl(24, 5%, 18%);
+
+ --Brown-800: hsl(14, 45%, 36%);
+
+ --Rose-800: hsl(332, 51%, 32%);
+ --Rose-50: hsl(330, 100%, 98%);
+}
+
+body {
+ font-family: "Outfit", sans-serif;
+ background-color: var(--Stone-100);
+ color: var(--Stone-600);
+ display: grid;
+ place-items: center;
+ max-width: 100vw;
+}
diff --git a/examples/recipePage/src/vite-env.d.ts b/examples/recipePage/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/examples/recipePage/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/recipePage/tsconfig.json b/examples/recipePage/tsconfig.json
new file mode 100644
index 00000000..75abdef2
--- /dev/null
+++ b/examples/recipePage/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/recipePage/vite.config.js b/examples/recipePage/vite.config.js
new file mode 100644
index 00000000..41713bec
--- /dev/null
+++ b/examples/recipePage/vite.config.js
@@ -0,0 +1,9 @@
+import { defineConfig } from "vite";
+
+export default defineConfig(({ command, mode, ssrBuild }) => {
+ return {
+ server: {
+ open: true,
+ },
+ };
+});