From ffd588ae169696edaff726855b41cc260f03c9b7 Mon Sep 17 00:00:00 2001
From: freshavocado7 <nikidimitriou97@gmail.com>
Date: Tue, 23 Apr 2024 16:20:54 +0200
Subject: [PATCH] style: Frontend overhaul with many small changes

---
 .pre-commit-config.yaml                       |   8 +
 CONTRIBUTING.md                               |   7 +-
 Dockerfile                                    |   2 +-
 README.md                                     |   4 +-
 capella_model_explorer/backend/explorer.py    |  19 +-
 entrypoint.sh                                 |   2 +-
 frontend/index.html                           |  27 +--
 frontend/package.json                         |  10 +-
 frontend/src/App.css                          |   2 +-
 frontend/src/components/Breadcrumbs.jsx       |  34 +--
 frontend/src/components/Button.jsx            |  12 +-
 frontend/src/components/Card.jsx              |  16 +-
 frontend/src/components/Header.jsx            |  25 ++-
 frontend/src/components/InstanceView.jsx      | 175 +++++++--------
 frontend/src/components/Spinner.jsx           |   8 +-
 frontend/src/components/TemplateCard.jsx      |  26 ++-
 frontend/src/components/TemplateDetails.jsx   | 207 +++++++++---------
 frontend/src/components/ThemeSwitcher.jsx     |  40 ++--
 frontend/src/components/ViewsList.jsx         |  56 +++--
 .../src/components/WiredTemplatesList.jsx     |  79 +++----
 frontend/src/index.css                        | 105 +++++++--
 frontend/src/views/HomeView.jsx               |  86 ++++----
 frontend/src/views/TemplateView.jsx           |  98 +++++----
 frontend/tailwind.config.js                   |  30 ++-
 templates/__generic__.html.j2                 |   2 +-
 templates/classes.html.j2                     |   1 -
 templates/common_macros.html.j2               |   2 +-
 templates/operational-activity.html.j2        |  24 +-
 templates/operational-capability.html.j2      |   5 +-
 templates/operational-entity.html.j2          |  12 +-
 templates/req.html.j2                         |   2 +-
 templates/req.yaml                            |   2 +-
 templates/system-capability.html.j2           |   2 +-
 templates/system_function.html.j2             |   2 +-
 34 files changed, 641 insertions(+), 491 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7c5703e..54253a2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -114,3 +114,11 @@ repos:
     hooks:
       - id: commitlint
         stages: [commit-msg]
+  - repo: https://github.com/pre-commit/mirrors-prettier
+    rev: v3.1.0
+    hooks:
+      - id: prettier
+        types_or: [ts, css, html, markdown]
+        additional_dependencies:
+          - 'prettier@^3.2.5'
+          - 'prettier-plugin-tailwindcss@^0.5.14'
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c98c895..b5dd31f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -76,8 +76,7 @@ The key differences are:
 
 - **Docstrings**: The [Numpy style guide] applies here.
 
-  [numpy style guide]:
-    https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard
+  [numpy style guide]: https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard
 
   When writing docstrings for functions, use the imperative style, as per
   [PEP-257]). For example, write "Do X and Y" instead of "Does X and Y".
@@ -124,7 +123,6 @@ The key differences are:
   typing related classes like `t.TypedDict`.
 
   <!-- prettier-ignore -->
-
   Use the new syntax and classes for typing introduced with Python 3.10.
 
   - Instead of `t.Tuple`, `t.List` etc. use the builtin classes `tuple`, `list`
@@ -142,8 +140,7 @@ The key differences are:
   If you have set up black correctly, you don't need to worry about this though
   :)
 
-  [black code style]:
-    https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html
+  [black code style]: https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html
 
 - When working with `dict`s, consider using `t.TypedDict` instead of a more
   generic `dict[str, float|int|str]`-like annotation where possible, as the
diff --git a/Dockerfile b/Dockerfile
index 1e8b0a8..5ba7f75 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -43,4 +43,4 @@ RUN chmod +x /entrypoint.sh
 ENV MODEL_ENTRYPOINT=/model
 RUN chmod -R 777 ./frontend/dist/
 
-ENTRYPOINT ["/entrypoint.sh"]
\ No newline at end of file
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/README.md b/README.md
index 5485cfa..2cd5ac5 100644
--- a/README.md
+++ b/README.md
@@ -16,8 +16,8 @@ We see a larger non-MBSE crowd struggling with the things hidden in the model. W
 
 **Use cases**:
 
--   Provide insights into / "spell-out" the model for non-MBSE stakeholders via document-a-like dynamic views that describe model elements in a human-readable form.
--   Provide meaningful default views (that can be further customized) for the key elements to kickstart the model exploration.
+- Provide insights into / "spell-out" the model for non-MBSE stakeholders via document-a-like dynamic views that describe model elements in a human-readable form.
+- Provide meaningful default views (that can be further customized) for the key elements to kickstart the model exploration.
 
 There are a few more use cases but we will reveal them a bit later.
 
diff --git a/capella_model_explorer/backend/explorer.py b/capella_model_explorer/backend/explorer.py
index a248726..bed0266 100644
--- a/capella_model_explorer/backend/explorer.py
+++ b/capella_model_explorer/backend/explorer.py
@@ -181,14 +181,23 @@ def read_template(template_name: str):
                     filters=filters,
                 )
                 base["objects"] = [
-                    {"idx": obj.uuid, 
-                     "name": str(
-                         obj.name if obj.name else (
-                             obj.long_name if hasattr(obj, "long_name") else "undefined")
-                    )} for obj in objects
+                    {
+                        "idx": obj.uuid,
+                        "name": str(
+                            obj.name
+                            if obj.name
+                            else (
+                                obj.long_name
+                                if hasattr(obj, "long_name")
+                                else "undefined"
+                            )
+                        ),
+                    }
+                    for obj in objects
                 ]
             except Exception as e:
                 import traceback
+
                 LOGGER.exception(
                     "Error finding objects for template %s", template_name
                 )
diff --git a/entrypoint.sh b/entrypoint.sh
index 29c0dbb..3ea167d 100755
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -7,4 +7,4 @@ sed -i "s|__ROUTE_PREFIX__|${ROUTE_PREFIX}|g" ./frontend/dist/static/env.js
 sed -i "s|href=\"/|href=\"${ROUTE_PREFIX}/|g" ./frontend/dist/index.html
 sed -i "s|src=\"/|src=\"${ROUTE_PREFIX}/|g" ./frontend/dist/index.html
 
-exec python -m capella_model_explorer.backend ${MODEL_ENTRYPOINT} /views
\ No newline at end of file
+exec python -m capella_model_explorer.backend ${MODEL_ENTRYPOINT} /views
diff --git a/frontend/index.html b/frontend/index.html
index aa9c08b..cbe16b5 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -3,20 +3,17 @@
  ~ SPDX-License-Identifier: Apache-2.0
  -->
 
-<!DOCTYPE html>
+<!doctype html>
 <html lang="en">
-    <head>
-        <meta charset="UTF-8" />
-        <link rel="icon" type="image/svg+xml" href="/static/vite.svg" />
-        <meta
-            name="viewport"
-            content="width=device-width, initial-scale=1.0"
-        />
-        <title>Model Explorer</title>
-    </head>
-    <body>
-        <div id="root"></div>
-        <script type="module" src="/src/main.jsx"></script>
-        <script src="/static/env.js"></script>
-    </body>
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/static/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Model Explorer</title>
+  </head>
+  <body>
+    <div id="root"></div>
+    <script type="module" src="/src/main.jsx"></script>
+    <script src="/static/env.js"></script>
+  </body>
 </html>
diff --git a/frontend/package.json b/frontend/package.json
index 5c348b2..a79afc5 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -2,7 +2,6 @@
   "name": "frontend",
   "private": true,
   "version": "0.0.0",
-  "type": "module",
   "scripts": {
     "dev": "vite",
     "build": "vite build",
@@ -12,9 +11,13 @@
     "build-storybook": "storybook build"
   },
   "dependencies": {
+    "lucide-react": "^0.372.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "react-zoom-pan-pinch": "^3.4.3"
+    "react-modal": "^3.16.1",
+    "react-responsive": "^10.0.0",
+    "react-zoom-pan-pinch": "^3.4.3",
+    "tailwind-scrollbar": "^3.1.0"
   },
   "devDependencies": {
     "@storybook/addon-essentials": "^7.6.16",
@@ -37,9 +40,10 @@
     "eslint-plugin-react-refresh": "^0.4.5",
     "eslint-plugin-storybook": "^0.8.0",
     "postcss": "^8.4.35",
+    "prettier": "^3.2.5",
+    "prettier-plugin-tailwindcss": "^0.5.14",
     "prop-types": "^15.8.1",
     "react-router-dom": "^6.22.2",
-    "react-responsive": "^10.0.0",
     "storybook": "^7.6.16",
     "tailwindcss": "^3.4.1",
     "vite": "^5.1.4",
diff --git a/frontend/src/App.css b/frontend/src/App.css
index 0cc3c92..cb5363c 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -16,7 +16,7 @@
   transition: filter 300ms;
 }
 .logo:hover {
-  filter: drop-shadow(0 0 2em #646cffaa);
+  filter: drop-shadow(0 0 2em #646cff);
 }
 .logo.react:hover {
   filter: drop-shadow(0 0 2em #61dafbaa);
diff --git a/frontend/src/components/Breadcrumbs.jsx b/frontend/src/components/Breadcrumbs.jsx
index 51516d7..0a1bf12 100644
--- a/frontend/src/components/Breadcrumbs.jsx
+++ b/frontend/src/components/Breadcrumbs.jsx
@@ -1,14 +1,14 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
 
-import React, { useEffect, useState } from 'react';
-import { Link, useLocation } from 'react-router-dom';
-import { API_BASE_URL } from '../APIConfig';
+import React, { useEffect, useState } from "react";
+import { Link, useLocation } from "react-router-dom";
+import { API_BASE_URL } from "../APIConfig";
 
 export const Breadcrumbs = () => {
   const location = useLocation();
   const [breadcrumbLabels, setBreadcrumbLabels] = useState({});
-  const pathnames = location.pathname.split('/').filter(x => x);
+  const pathnames = location.pathname.split("/").filter((x) => x);
   const [error, setError] = useState(null);
 
   const fetchModelInfo = async () => {
@@ -23,9 +23,9 @@ export const Breadcrumbs = () => {
 
     const viewsDict = await response.json();
     const allViews = Object.values(viewsDict).flat();
-    const view = allViews.find(v => v.idx.toString() === idx);
+    const view = allViews.find((v) => v.idx.toString() === idx);
     return view ? view.name : idx;
-};
+  };
 
   // Function to fetch object names
   const fetchObjectName = async (uuid) => {
@@ -37,10 +37,10 @@ export const Breadcrumbs = () => {
   useEffect(() => {
     const updateLabels = async () => {
       const title = await fetchModelInfo();
-      const labels = { '/': title };
+      const labels = { "/": title };
 
       for (let i = 0; i < pathnames.length; i++) {
-        const to = `/${pathnames.slice(0, i + 1).join('/')}`;
+        const to = `/${pathnames.slice(0, i + 1).join("/")}`;
 
         if (i === 0) {
           labels[to] = await fetchViewName(pathnames[i]);
@@ -58,24 +58,30 @@ export const Breadcrumbs = () => {
     updateLabels();
   }, [location]);
 
-  const visible_pathnames = [breadcrumbLabels['/'], ...location.pathname.split('/').filter(x => x)];
+  const visible_pathnames = [
+    breadcrumbLabels["/"],
+    ...location.pathname.split("/").filter((x) => x),
+  ];
 
   return (
-    <nav aria-label="breadcrumb" className="flex items-center">
+    <nav
+      aria-label="breadcrumb"
+      className="flex items-center text-black dark:text-gray-200 font-medium"
+    >
       <ol className="flex items-center">
-        <li className="flex items-center dark:text-gray-200">
-          <Link to={"/"}>{breadcrumbLabels['/']}</Link>
+        <li className="flex items-center">
+          <Link to={"/"}>{breadcrumbLabels["/"]}</Link>
           <span className="mx-2">/</span>
         </li>
         {visible_pathnames.slice(1).map((value, index) => {
           const last = index === visible_pathnames.length - 2;
-          const to = `/${visible_pathnames.slice(1, index + 2).join('/')}`;
+          const to = `/${visible_pathnames.slice(1, index + 2).join("/")}`;
           const label = breadcrumbLabels[to] || value;
 
           return (
             <li className="flex items-center" key={to}>
               {!last && <Link to={to}>{label}</Link>}
-              {last && <span className='dark:text-gray-200' >{label}</span>}
+              {last && <span className=" text-custom-blue">{label}</span>}
               {!last && <span className="mx-2">/</span>}
             </li>
           );
diff --git a/frontend/src/components/Button.jsx b/frontend/src/components/Button.jsx
index e8b6791..1a1d0e8 100644
--- a/frontend/src/components/Button.jsx
+++ b/frontend/src/components/Button.jsx
@@ -1,12 +1,16 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
 
-import React from 'react';
+import React from "react";
 
 export const Button = ({ theme, children, ...props }) => {
   return (
-    <a href="#" {...props} className="print:hidden rounded-md mx-1 bg-blue-800 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 hover:text-gray-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ">
-        {children}
+    <a
+      href="#"
+      {...props}
+      className="print:hidden rounded-md mx-1 bg-custom-blue px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm dark:shadow-dark hover:bg-custom-blue-hover hover:text-gray-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
+    >
+      {children}
     </a>
   );
-};
\ No newline at end of file
+};
diff --git a/frontend/src/components/Card.jsx b/frontend/src/components/Card.jsx
index b97d49a..f0955e4 100644
--- a/frontend/src/components/Card.jsx
+++ b/frontend/src/components/Card.jsx
@@ -1,11 +1,13 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
 
-import React from 'react';
+import React from "react";
 
-export const Card = ({children, onClick}) => (
-    <div onClick={onClick}
-    className='max-w-sm bg-white rounded-lg border border-gray-200 shadow-md m-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 dark:bg-gray-800 dark:border-gray-700'>
-        {children}
-    </div>
-    );
+export const Card = ({ children, onClick }) => (
+  <div
+    onClick={onClick}
+    className="m-2 max-w-sm cursor-pointer rounded-lg border border-gray-200 bg-white shadow-md hover:bg-gray-100 dark:border-gray-700 dark:bg-gray-800 dark:shadow-dark dark:hover:bg-gray-700"
+  >
+    {children}
+  </div>
+);
diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx
index 1cad8e3..33e968a 100644
--- a/frontend/src/components/Header.jsx
+++ b/frontend/src/components/Header.jsx
@@ -2,15 +2,20 @@
 // SPDX-License-Identifier: Apache-2.0
 
 import React from "react";
-import {Breadcrumbs} from "./Breadcrumbs";
-import {ThemeSwitcher} from "./ThemeSwitcher";
+import { Breadcrumbs } from "./Breadcrumbs";
+import { ThemeSwitcher } from "./ThemeSwitcher";
 
 export const Header = () => {
-    return (
-      <header className="print:hidden text-gray-700 p-4 flex justify-between items-center">
-          <div><Breadcrumbs /></div>
-          <div></div>
-          <div><ThemeSwitcher /></div>
-      </header>
-    );
-  };
+  return (
+    <header className="flex items-center justify-between rounded-b-lg bg-gray-100 p-6 text-lg text-white shadow-lg dark:bg-custom-dark-2 dark:shadow-dark print:hidden">
+      {" "}
+      <div>
+        <Breadcrumbs />
+      </div>
+      <div></div>
+      <div>
+        <ThemeSwitcher />
+      </div>
+    </header>
+  );
+};
diff --git a/frontend/src/components/InstanceView.jsx b/frontend/src/components/InstanceView.jsx
index d1e41e9..e17bf9f 100644
--- a/frontend/src/components/InstanceView.jsx
+++ b/frontend/src/components/InstanceView.jsx
@@ -7,97 +7,92 @@ import { Spinner } from "./Spinner";
 import { Button } from "./Button";
 
 export const InstanceView = ({ templateName, objectID, endpoint }) => {
-    const [details, setDetails] = useState([]);
-    const [loading, setLoading] = useState(true);
-    const contentRef = useRef(null);
-    const [isHovering, setIsHovering] = useState(false);
+  const [details, setDetails] = useState([]);
+  const [loading, setLoading] = useState(true);
+  const contentRef = useRef(null);
+  const [isHovering, setIsHovering] = useState(false);
 
-    useEffect(() => {
-        setLoading(true);
-        const url = endpoint + `${templateName}/${objectID}`;
-        fetch(url, {
-            method: "GET",
-            headers: {
-                "Content-Type": "text/html",
-            },
-        })
-            .then((response) => response.text())
-            .then((data) => {
-                const parser = new DOMParser();
-                const doc = parser.parseFromString(data, "text/html");
-                const contentItems = [];
-                doc.body.childNodes.forEach((node) => {
-                    if (node.nodeType === Node.ELEMENT_NODE) {
-                        if (node.tagName === "svg") {
-                            contentItems.push({
-                                type: "SVGDisplay",
-                                content: node.outerHTML,
-                            });
-                        } else {
-                            contentItems.push({
-                                type: "HTML",
-                                content: node.outerHTML,
-                            });
-                        }
-                    }
-                });
-                setDetails(contentItems);
-                setLoading(false);
-                if (contentRef.current)
-                    contentRef.current.scrollIntoView();
-            })
-            .catch((error) => {
-                setLoading(false);
-                setDetails("Error fetching data ", error);
-            });
-    }, [endpoint, objectID, templateName]);
-    if (loading)
-        return (
-            <div>
-                <Spinner />
-            </div>
-        );
+  useEffect(() => {
+    setLoading(true);
+    const url = endpoint + `${templateName}/${objectID}`;
+    fetch(url, {
+      method: "GET",
+      headers: {
+        "Content-Type": "text/html",
+      },
+    })
+      .then((response) => response.text())
+      .then((data) => {
+        const parser = new DOMParser();
+        const doc = parser.parseFromString(data, "text/html");
+        const contentItems = [];
+        doc.body.childNodes.forEach((node) => {
+          if (node.nodeType === Node.ELEMENT_NODE) {
+            if (node.tagName === "svg") {
+              contentItems.push({
+                type: "SVGDisplay",
+                content: node.outerHTML,
+              });
+            } else {
+              contentItems.push({
+                type: "HTML",
+                content: node.outerHTML,
+              });
+            }
+          }
+        });
+        setDetails(contentItems);
+        setLoading(false);
+        if (contentRef.current) contentRef.current.scrollIntoView();
+      })
+      .catch((error) => {
+        setLoading(false);
+        setDetails("Error fetching data ", error);
+      });
+  }, [endpoint, objectID, templateName]);
+  if (loading)
     return (
-        <div
-            ref={contentRef}
-            className={`html-content bg-white shadow-lg dark:shadow-white text-gray-700 mx-auto md:my-8 p-8 md:w-[210mm] max-w-full max-h-full overflow-auto print:shadow-none print:m-0 print:p-0 print:bg-transparent relative box-border border-4 ${
-                isHovering
-                    ? "border-grey-700 shadow-md z-50"
-                    : "border-transparent"
-            }`}
-            onMouseEnter={() => setIsHovering(true)}
-            onMouseLeave={() => setIsHovering(false)}
-        >
-            {isHovering && (
-                <Button
-                    style={{
-                        position: "absolute",
-                        top: 0,
-                        right: 0,
-                        margin: "1rem",
-                        padding: "0.5rem",
-                    }}
-                    onClick={() => window.print()}
-                >
-                    Print Content
-                </Button>
-            )}
-            {details.map((item, idx) => {
-                if (item.type === "SVGDisplay") {
-                    return (
-                        <SVGDisplay key={idx} content={item.content} />
-                    );
-                } else {
-                    return (
-                        <div
-                            key={idx}
-                            dangerouslySetInnerHTML={{
-                                __html: item.content,
-                            }}
-                        />
-                    );
-                }
-            })}
-        </div>
+      <div className="mx-auto md:w-[210mm]">
+        <Spinner />
+      </div>
     );
+  return (
+    <div
+      ref={contentRef}
+      className={`html-content relative mx-auto box-border h-[75vh] max-h-full overflow-auto rounded-lg border-4 border-transparent bg-gray-100 p-8 text-gray-700 shadow-lg scrollbar scrollbar-track-gray-200 scrollbar-thumb-gray-400 hover:border-gray-300 dark:bg-custom-dark-2 dark:text-gray-100 dark:shadow-dark dark:scrollbar-track-custom-dark-3 dark:scrollbar-thumb-slate-600 md:w-[210mm] print:m-0 print:bg-transparent print:p-0 print:shadow-none ${
+        isHovering ? "z-50 shadow-md" : ""
+      }`}
+      onMouseEnter={() => setIsHovering(true)}
+      onMouseLeave={() => setIsHovering(false)}
+    >
+      {isHovering && (
+        <Button
+          style={{
+            position: "absolute",
+            top: 0,
+            right: 0,
+            margin: "1rem",
+            padding: "0.5rem",
+          }}
+          onClick={() => window.print()}
+        >
+          Print Content
+        </Button>
+      )}
+      {details.map((item, idx) => {
+        if (item.type === "SVGDisplay") {
+          return <SVGDisplay key={idx} content={item.content} />;
+        } else {
+          return (
+            <div
+              key={idx}
+              dangerouslySetInnerHTML={{
+                __html: item.content,
+              }}
+            />
+          );
+        }
+      })}
+    </div>
+  );
 };
diff --git a/frontend/src/components/Spinner.jsx b/frontend/src/components/Spinner.jsx
index 4d45258..7cea3b4 100644
--- a/frontend/src/components/Spinner.jsx
+++ b/frontend/src/components/Spinner.jsx
@@ -1,10 +1,10 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
 
-import React from 'react';
+import React from "react";
 
 export const Spinner = ({}) => (
-    <div className='flex justify-center items-center'>
-        <div className='animate-spin-slow rounded-full h-12 w-12 border-t-4 border-b-4 border-sky-500 ease-linear'></div>
-    </div>
+  <div className="mt-32 flex items-center justify-center">
+    <div className="h-12 w-12 animate-spin-slow rounded-full border-b-4 border-t-4 border-custom-blue ease-linear"></div>
+  </div>
 );
diff --git a/frontend/src/components/TemplateCard.jsx b/frontend/src/components/TemplateCard.jsx
index 1f1740f..6b402f6 100644
--- a/frontend/src/components/TemplateCard.jsx
+++ b/frontend/src/components/TemplateCard.jsx
@@ -1,16 +1,20 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
 
-import React from 'react';
+import React from "react";
 
-export const TemplateCard = ({template, onClickCallback}) => (
-    <div onClick={() => onClickCallback(template.idx)}
-        className='max-w-sm bg-white rounded-lg border border-gray-200 shadow-md m-2 cursor-pointer hover:bg-gray-100'>
-        <div className='p-5'>
-            <h5 className='mb-2 text-2xl font-bold text-gray-900'>
-                {template.name}
-            </h5>
-            <p className='mb-3 font-normal text-gray-700'>{template.description}</p>
-        </div>
+export const TemplateCard = ({ template, onClickCallback }) => (
+  <div
+    onClick={() => onClickCallback(template.idx)}
+    className="m-2 mt-6 max-w-sm cursor-pointer rounded-lg bg-gray-300 shadow-md hover:bg-custom-light dark:bg-custom-dark-2 dark:shadow-dark dark:hover:bg-custom-dark-4"
+  >
+    <div className="p-5">
+      <h5 className="mb-2 text-2xl font-bold text-gray-900 dark:text-gray-100">
+        {template.name}
+      </h5>
+      <p className="mb-3 font-normal text-gray-700 dark:text-gray-300">
+        {template.description}
+      </p>
     </div>
-    );
+  </div>
+);
diff --git a/frontend/src/components/TemplateDetails.jsx b/frontend/src/components/TemplateDetails.jsx
index dd51e83..4c2dd17 100644
--- a/frontend/src/components/TemplateDetails.jsx
+++ b/frontend/src/components/TemplateDetails.jsx
@@ -5,114 +5,111 @@ import React, { useEffect, useState } from "react";
 import { useNavigate, useParams } from "react-router-dom";
 
 export const TemplateDetails = ({ endpoint, onSingleInstance }) => {
-    let { templateName, objectID } = useParams();
-    const [error, setError] = useState(null);
-    const [details, setDetails] = useState([]);
-    const navigate = useNavigate();
-    const [filterText, setFilterText] = useState("");
+  let { templateName, objectID } = useParams();
+  const [error, setError] = useState(null);
+  const [details, setDetails] = useState([]);
+  const navigate = useNavigate();
+  const [filterText, setFilterText] = useState("");
 
-    useEffect(() => {
-        const fetchDetails = async () => {
-            try {
-                const response = await fetch(endpoint + templateName, {
-                    method: "GET",
-                    headers: {
-                        "Content-Type": "application/json",
-                    },
-                });
-                const data = await response.json();
-                setDetails(data);
-                if (data.single) {
-                    onSingleInstance("render");
-                }
-            } catch (error) {
-                setError(error.message);
-            } finally {
-            }
-        };
-        fetchDetails();
-    }, [endpoint, templateName, objectID, onSingleInstance]);
-    if (error) {
-        return (
-            <div className="bg-red-500 text-white p-4 rounded text-2xl">
-                {error}
-            </div>
-        );
-    }
+  useEffect(() => {
+    const fetchDetails = async () => {
+      try {
+        const response = await fetch(endpoint + templateName, {
+          method: "GET",
+          headers: {
+            "Content-Type": "application/json",
+          },
+        });
+        const data = await response.json();
+        setDetails(data);
+        if (data.single) {
+          onSingleInstance("render");
+        }
+      } catch (error) {
+        setError(error.message);
+      } finally {
+      }
+    };
+    fetchDetails();
+  }, [endpoint, templateName, objectID, onSingleInstance]);
+  if (error) {
     return (
-        <div className="flex flex-col h-full">
-            <h5 className="mb-2 text-2xl font-bold text-gray-900 dark:text-white">
-                {details.name}
-            </h5>
-            <p className="mb-3 font-normal text-gray-700 dark:text-white">
-                {details.description}
-            </p>
-            {details.error ? (
-                <div>
-                    <p>
-                        We failed to find matching template instances due to
-                        the following error:{" "}
-                    </p>
-                    <div className="bg-red-500 py-2 px-2 text-white rounded">
-                        {details.error}
-                    </div>
-                </div>
+      <div className="dark:bg-custom-dark-error rounded bg-red-500 p-4 text-2xl text-white">
+        {error}
+      </div>
+    );
+  }
+  return (
+    <div className="flex h-full max-h-[calc(90vh-32px)] flex-col overflow-hidden rounded-lg bg-gray-100 p-4 shadow-lg dark:bg-custom-dark-2  dark:shadow-dark">
+      <h5 className="mb-2 text-2xl font-bold text-gray-900 dark:text-gray-100 ">
+        {details.name}
+      </h5>
+      <p className="mb-3 font-normal text-gray-700 dark:text-gray-300">
+        {details.description}
+      </p>
+      {details.error ? (
+        <div>
+          <p>
+            We failed to find matching template instances due to the following
+            error:{" "}
+          </p>
+          <div className="dark:bg-custom-dark-error rounded bg-red-500 px-2 py-2 text-white">
+            {details.error}
+          </div>
+        </div>
+      ) : (
+        <>
+          {details.single === false ? (
+            <input
+              type="text"
+              value={filterText}
+              onChange={(e) => setFilterText(e.target.value)}
+              placeholder="Filter objects"
+              className="mx-auto mb-3 mr-6 w-80 rounded border-2 border-gray-300 p-2 shadow-sm dark:border-gray-500 dark:bg-custom-dark-3"
+            />
+          ) : (
+            <></>
+          )}
+          <div className="flex flex-wrap items-center justify-center overflow-auto border-2 border-transparent text-left scrollbar scrollbar-track-gray-200 scrollbar-thumb-gray-400 dark:scrollbar-track-custom-dark-3 dark:scrollbar-thumb-slate-600 ">
+            {details.objects &&
+            details.single === false &&
+            details.objects.length === 0 ? (
+              <p>No objects found</p>
             ) : (
-                <>
-                    {details.single === false ? (
-                    <input
-                        type="text"
-                        value={filterText}
-                        onChange={(e) => setFilterText(e.target.value)}
-                        placeholder="Filter objects"
-                        className="mb-3 p-2 border rounded"
-                    />):( <></>)}
-                    <div className="flex flex-wrap justify-center items-center overflow-auto">
-                        {details.objects && details.single === false && details.objects.length === 0 ? (
-                            <p>No objects found</p>
-                        ) : (
-                            details.objects &&
-                            details.objects
-                                .filter((object) =>
-                                    object.name
-                                        .toLowerCase()
-                                        .includes(filterText.toLowerCase())
-                                )
-                                .map((object) => (
-                                    <div
-                                        key={object.idx}
-                                        onClick={() => {
-                                            navigate(
-                                                `/${templateName}/${object.idx}`
-                                            );
-                                        }}
-                                        className={
-                                            (objectID &&
-                                            object.idx === objectID
-                                                ? "bg-blue-800 dark:bg-blue-800 text-white hover:dark:text-white hover:text-blue-800"
-                                                : "text-gray-900") +
-                                            " max-w-sm rounded-lg border border-gray-200 shadow-md m-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 dark:border-gray-700"
-                                        }
-                                    >
-                                        <div className="p-2">
-                                            <h5
-                                                className={
-                                                    "text-md font-bold dark:text-white " +
-                                                    (objectID &&
-                                                    object.idx === objectID
-                                                        ? ""
-                                                        : "")
-                                                }
-                                            >
-                                                {object.name}
-                                            </h5>
-                                        </div>
-                                    </div>
-                                ))
-                        )}
+              details.objects &&
+              details.objects
+                .filter((object) =>
+                  object.name.toLowerCase().includes(filterText.toLowerCase()),
+                )
+                .map((object) => (
+                  <div
+                    key={object.idx}
+                    onClick={() => {
+                      navigate(`/${templateName}/${object.idx}`);
+                    }}
+                    className={
+                      (objectID && object.idx === objectID
+                        ? "w-full bg-custom-blue text-white dark:bg-custom-blue dark:text-gray-100"
+                        : "w-full bg-gray-200 text-gray-900 dark:bg-custom-dark-4") +
+                      " dark:bg-dark-quaternary m-2 max-w-sm cursor-pointer rounded-lg shadow-md hover:bg-custom-blue hover:text-white dark:border-gray-700 dark:shadow-dark dark:hover:bg-blue-500 "
+                    }
+                  >
+                    <div className="p-2">
+                      <h5
+                        className={
+                          "text-md font-bold dark:text-gray-100" +
+                          (objectID && object.idx === objectID ? "" : "")
+                        }
+                      >
+                        {object.name}
+                      </h5>
                     </div>
-                </>
+                  </div>
+                ))
             )}
-        </div>
-    );
+          </div>
+        </>
+      )}
+    </div>
+  );
 };
diff --git a/frontend/src/components/ThemeSwitcher.jsx b/frontend/src/components/ThemeSwitcher.jsx
index 8072bfd..1a20e9b 100644
--- a/frontend/src/components/ThemeSwitcher.jsx
+++ b/frontend/src/components/ThemeSwitcher.jsx
@@ -1,29 +1,43 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
-
-import React, { useState, useEffect } from 'react';
-import { Button } from './Button';
+import React, { useState, useEffect } from "react";
+import { Sun, Moon } from "lucide-react";
 
 export const ThemeSwitcher = () => {
-  const [theme, setTheme] = useState('light'); // default theme is light
+  const systemTheme =
+    window.matchMedia &&
+    window.matchMedia("(prefers-color-scheme: dark)").matches
+      ? "dark"
+      : "light";
+
+  const savedTheme = localStorage.getItem("theme") || systemTheme;
+  const [theme, setTheme] = useState(savedTheme);
 
   useEffect(() => {
-    if (theme === 'dark') {
-      document.documentElement.classList.add('dark');
+    if (theme === "dark") {
+      document.documentElement.classList.add("dark");
+      document.documentElement.classList.remove("light");
     } else {
-      document.documentElement.classList.remove('dark');
+      document.documentElement.classList.add("light");
+      document.documentElement.classList.remove("dark");
     }
+    localStorage.setItem("theme", theme);
   }, [theme]);
 
-  const switchTheme = (newTheme) => {
-    setTheme(newTheme);
+  const toggleTheme = () => {
+    setTheme(theme === "dark" ? "light" : "dark");
   };
 
   return (
-    <div>
-      {theme !== 'light' && <Button onClick={() => switchTheme('light')}>Light</Button>}
-      {theme !== 'dark' && <Button onClick={() => switchTheme('dark')}>Dark</Button>}
-      {theme !== 'auto' && <Button onClick={() => switchTheme('auto')}>Auto</Button>}
+    <div
+      onClick={toggleTheme}
+      className="flex cursor-pointer items-center justify-center rounded-full bg-custom-light p-2 transition-colors duration-700 ease-in-out hover:bg-custom-dark-4 dark:bg-custom-dark-1 dark:hover:bg-custom-light"
+    >
+      {theme === "dark" ? (
+        <Sun className="transform text-orange-500 transition-transform duration-500 ease-in-out" />
+      ) : (
+        <Moon className="transform text-black transition-transform duration-500 ease-in-out" />
+      )}
     </div>
   );
 };
diff --git a/frontend/src/components/ViewsList.jsx b/frontend/src/components/ViewsList.jsx
index f74e66d..766ff24 100644
--- a/frontend/src/components/ViewsList.jsx
+++ b/frontend/src/components/ViewsList.jsx
@@ -1,30 +1,44 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
 
-import React from 'react';
-import { TemplateCard } from './TemplateCard';
+import React from "react";
+import { TemplateCard } from "./TemplateCard";
 
-export const ViewsList = ({templates, cardClickCallback}) => {
+export const ViewsList = ({ templates, cardClickCallback }) => {
   let categories = {
-    "oa": "Operational Analysis Reports",
-    "sa": "System Analysis Reports",
-    "la": "Logical Architecture Reports",
-    "pa": "Physical Architecture Reports",
-    "xc": "Cross-cutting Reports",
-    "other": "Other reports"};
+    oa: "Operational Analysis Reports",
+    sa: "System Analysis Reports",
+    la: "Logical Architecture Reports",
+    pa: "Physical Architecture Reports",
+    xc: "Cross-cutting Reports",
+    other: "Other reports",
+  };
+
   return (
-    <div>
-      {Object.keys(categories).map(cat => (
-        (cat in templates) && (templates[cat].length > 0) && (
-        <div key={cat}>
-        <h2 key={cat + "h2"} className='text-2xl p-2'>{categories[cat]}</h2>
-        <div key={cat + "div"} className='flex flex-wrap justify-center gap-y-2'>
-            {templates[cat].map(template => (
-              <TemplateCard key={template.idx} template={template} onClickCallback={cardClickCallback} />
-            ))}
-        </div>
-        </div>)
-      ))}
+    <div className="rounded-lg bg-gray-100 pb-2 pl-4 pr-4 pt-8 shadow-lg dark:bg-custom-dark-3">
+      {Object.keys(categories).map((cat) => {
+        if (cat in templates && templates[cat].length > 0) {
+          return (
+            <div key={cat} className="mb-8 p-4  ">
+              <h2 className="border-b border-gray-900 pb-2 text-2xl font-bold dark:border-gray-300">
+                <span className=" px-4 py-2 text-gray-900 dark:text-gray-100">
+                  {categories[cat]}
+                </span>
+              </h2>
+              <div className="mt-2 flex flex-wrap justify-center gap-y-2">
+                {templates[cat].map((template) => (
+                  <TemplateCard
+                    key={template.idx}
+                    template={template}
+                    onClickCallback={cardClickCallback}
+                  />
+                ))}
+              </div>
+            </div>
+          );
+        }
+        return null;
+      })}
     </div>
   );
 };
diff --git a/frontend/src/components/WiredTemplatesList.jsx b/frontend/src/components/WiredTemplatesList.jsx
index 08b07df..060bea7 100644
--- a/frontend/src/components/WiredTemplatesList.jsx
+++ b/frontend/src/components/WiredTemplatesList.jsx
@@ -1,45 +1,48 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
 
-import React, {useState, useEffect} from 'react';
-import { useNavigate } from 'react-router-dom';
-import { ViewsList } from './ViewsList';
-import { API_BASE_URL } from '../APIConfig';
-
+import React, { useState, useEffect } from "react";
+import { useNavigate } from "react-router-dom";
+import { ViewsList } from "./ViewsList";
+import { API_BASE_URL } from "../APIConfig";
 
 export const WiredTemplatesList = () => {
-    const [templates, setTemplates] = useState([])
-    const [error, setError] = useState(null);
-    const navigate = useNavigate();
+  const [templates, setTemplates] = useState([]);
+  const [error, setError] = useState(null);
+  const navigate = useNavigate();
 
-    useEffect(() => {
-        const fetchTemplates = async () => {
-            try {
-                const response = await fetch(API_BASE_URL + "/views", {
-                    method: 'GET',
-                    headers: {
-                        'Content-Type': 'application/json'
-                    },
-                });
-                const data = await response.json();
-                setTemplates(data);
-            }
-            catch (error) {
-                setError(error.message)
-            }
-            finally {}
-        };
-        fetchTemplates();
-    }, []);
+  useEffect(() => {
+    const fetchTemplates = async () => {
+      try {
+        const response = await fetch(API_BASE_URL + "/views", {
+          method: "GET",
+          headers: {
+            "Content-Type": "application/json",
+          },
+        });
+        const data = await response.json();
+        setTemplates(data);
+      } catch (error) {
+        setError(error.message);
+      } finally {
+      }
+    };
+    fetchTemplates();
+  }, []);
 
-    if (error) {
-        return (
-        <div className='bg-red-500 text-white p-4 rounded text-2xl'>
-            {error === 'Failed to fetch' ?
-            "Can't connect to the server. Maybe your session was inactive for too long? if that's the case, request a new session / restart the app."
-            :
-            error
-            }
-        </div>);
-    }
-    return <ViewsList templates={templates} cardClickCallback={(idx) => navigate(`/${idx}`, {state: {idx: idx}})} />;}
+  if (error) {
+    return (
+      <div className="dark:bg-custom-dark-error rounded bg-red-500 p-4 text-2xl text-white">
+        {error === "Failed to fetch"
+          ? "Can't connect to the server. Maybe your session was inactive for too long? if that's the case, request a new session / restart the app."
+          : error}
+      </div>
+    );
+  }
+  return (
+    <ViewsList
+      templates={templates}
+      cardClickCallback={(idx) => navigate(`/${idx}`, { state: { idx: idx } })}
+    />
+  );
+};
diff --git a/frontend/src/index.css b/frontend/src/index.css
index cf36d75..76ac967 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -7,7 +7,6 @@
 @tailwind components;
 @tailwind utilities;
 
-
 :root {
   font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
   line-height: 1.5;
@@ -15,7 +14,6 @@
 
   color-scheme: light dark;
   color: rgba(255, 255, 255, 0.87);
-  background-color: #242424;
 
   font-synthesis: none;
   text-rendering: optimizeLegibility;
@@ -23,22 +21,13 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
-a {
-  font-weight: 500;
-  color: #646cff;
-  text-decoration: inherit;
-}
-a:hover {
-  color: #535bf2;
-}
-
 body {
   margin: 0;
   display: flex;
   place-items: center;
   min-width: 320px;
   min-height: 100vh;
-  @apply bg-white dark:bg-gray-900;
+  @apply bg-custom-light dark:bg-custom-dark-1;
 }
 
 .react-transform-wrapper {
@@ -78,47 +67,58 @@ button:focus-visible {
     color: #747bff;
   }
   button {
-    background-color: #f9f9f9;
+    background-color: blue;
   }
 }
 
 .collapsed {
-  width: 50px; 
+  width: 50px;
 }
 
 .html-content {
   text-align: left;
-
 }
 
 .html-content svg {
   display: block;
-  @apply max-w-full h-auto;
+  @apply h-auto max-w-full;
   text-align: center;
 }
 
 .html-content h1 {
-  @apply text-4xl mb-4 mt-5;
+  @apply mb-4 mt-5 text-4xl;
 }
 
 .html-content h2 {
-  @apply text-3xl mb-3 mt-5;
+  @apply mb-3 mt-5 text-3xl;
 }
 
 .html-content h3 {
-  @apply text-2xl mb-3 mt-5;
+  @apply mb-3 mt-5 text-2xl;
 }
 
-.html-content table { @apply min-w-full divide-y divide-gray-200; }
-.html-content p { margin-top: 0.75em; margin-bottom: 0.75em; line-height: 1.5em; }
-.html-content p a, .html-content li a { @apply text-gray-500 italic hover:underline; }
+.html-content table {
+  @apply min-w-full divide-y divide-gray-200;
+}
+.html-content p {
+  margin-top: 0.75em;
+  margin-bottom: 0.75em;
+  line-height: 1.5em;
+}
+.html-content p a,
+.html-content li a {
+  @apply italic text-gray-500 hover:underline;
+}
 
 ul {
   @apply list-disc pl-5;
 }
 
 @media print {
-  body, .html-wrapper, .flex-1, .html-content {
+  body,
+  .html-wrapper,
+  .flex-1,
+  .html-content {
     overflow: visible !important;
   }
   .print\\:hidden {
@@ -128,5 +128,62 @@ ul {
     border: none !important;
     box-shadow: none !important;
   }
-  @page  { margin: 15mm; }   
+  @page {
+    margin: 15mm;
+  }
+}
+
+.switch {
+  position: relative;
+  display: inline-block;
+  width: 60px;
+  height: 34px;
+}
+
+.switch input {
+  opacity: 0;
+  width: 0;
+  height: 0;
+}
+
+.slider {
+  position: absolute;
+  cursor: pointer;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: #ccc;
+  transition: 0.4s;
+}
+
+.slider:before {
+  position: absolute;
+  content: "";
+  height: 26px;
+  width: 26px;
+  left: 4px;
+  bottom: 4px;
+  background-color: white;
+  transition: 0.4s;
+}
+
+input:checked + .slider {
+  background-color: #2196f3;
+}
+
+input:focus + .slider {
+  box-shadow: 0 0 1px #2196f3;
+}
+
+input:checked + .slider:before {
+  transform: translateX(26px);
+}
+
+.slider.round {
+  border-radius: 34px;
+}
+
+.slider.round:before {
+  border-radius: 50%;
 }
diff --git a/frontend/src/views/HomeView.jsx b/frontend/src/views/HomeView.jsx
index e933955..d07d7d9 100644
--- a/frontend/src/views/HomeView.jsx
+++ b/frontend/src/views/HomeView.jsx
@@ -1,49 +1,61 @@
 // Copyright DB InfraGO AG and contributors
 // SPDX-License-Identifier: Apache-2.0
 
-import React, { useState, useEffect } from 'react';
-import {WiredTemplatesList} from '../components/WiredTemplatesList';
-import { API_BASE_URL } from '../APIConfig';
+import React, { useState, useEffect } from "react";
+import { WiredTemplatesList } from "../components/WiredTemplatesList";
+import { API_BASE_URL } from "../APIConfig";
+import { ThemeSwitcher } from "../components/ThemeSwitcher";
 
 export const HomeView = () => {
-    const [modelInfo, setModelInfo] = useState(null);
-    const [error, setError] = useState(null);
+  const [modelInfo, setModelInfo] = useState(null);
+  const [error, setError] = useState(null);
 
-    useEffect(() => {
-        const fetchModelInfo = async () => {
-            try {
-                const response = await fetch(API_BASE_URL + '/model-info');
-                const data = await response.json();
-                setModelInfo(data);
-            } catch (err) {
-                setError('Failed to fetch model info: ' + err.message);
-            }
-        };
+  useEffect(() => {
+    const fetchModelInfo = async () => {
+      try {
+        const response = await fetch(API_BASE_URL + "/model-info");
+        const data = await response.json();
+        setModelInfo(data);
+      } catch (err) {
+        setError("Failed to fetch model info: " + err.message);
+      }
+    };
 
-        fetchModelInfo();
-    }, []);
-
-    if (error) {
-        return (<div className='bg-red-500 text-white text-xl p-2 rounded'>{error}</div>);
-    }
+    fetchModelInfo();
+  }, []);
 
+  if (error) {
     return (
-        <div className="flex flex-col justify-center h-full">
-            <div className="mb-4  mx-auto text-center">
-                {modelInfo && (
-                    <div className='bg-white dark:bg-gray-900 text-gray-700 dark:text-white'>
-                        <h2 className='text-xl'>{modelInfo.title}</h2>
-                        {modelInfo.capella_version && <p>Capella Version: {modelInfo.capella_version}</p>}
-                        {modelInfo.revision && <p>Revision: {modelInfo.revision}</p>}
-                        {modelInfo.branch && <p>Branch: {modelInfo.branch}</p>}
-                        {modelInfo.hash && <p>Commit Hash: {modelInfo.hash}</p>}
-                        <div dangerouslySetInnerHTML={{ __html: modelInfo.badge }}></div>
-                    </div>
-                )}
-            </div>
-            <div>
-                <WiredTemplatesList />
+      <div className="dark:bg-custom-dark-error rounded bg-red-500 p-2 text-xl text-white">
+        {error}
+      </div>
+    );
+  }
+
+  return (
+    <div className="mb-8 flex h-full flex-col justify-center">
+      <div className="flex w-full items-start justify-between px-4 ">
+        <div className="mx-auto mb-8 text-center ">
+          {modelInfo && (
+            <div className="rounded-b-lg bg-gray-100 p-4 text-gray-700 shadow-lg dark:bg-custom-dark-3 dark:text-gray-100 ">
+              <h2 className="text-xl">{modelInfo.title}</h2>
+              {modelInfo.capella_version && (
+                <p>Capella Version: {modelInfo.capella_version}</p>
+              )}
+              {modelInfo.revision && <p>Revision: {modelInfo.revision}</p>}
+              {modelInfo.branch && <p>Branch: {modelInfo.branch}</p>}
+              {modelInfo.hash && <p>Commit Hash: {modelInfo.hash}</p>}
+              <div dangerouslySetInnerHTML={{ __html: modelInfo.badge }}></div>
             </div>
+          )}
         </div>
-    );
+        <div className="flex items-start justify-end rounded-b-lg bg-gray-100 p-6 shadow-lg dark:bg-custom-dark-2">
+          <ThemeSwitcher />
+        </div>
+      </div>
+      <div className="mt-4">
+        <WiredTemplatesList />
+      </div>
+    </div>
+  );
 };
diff --git a/frontend/src/views/TemplateView.jsx b/frontend/src/views/TemplateView.jsx
index b1396a5..6e9be88 100644
--- a/frontend/src/views/TemplateView.jsx
+++ b/frontend/src/views/TemplateView.jsx
@@ -9,59 +9,63 @@ In this component we show list of template instances, and when we click on a tem
 
 import React, { useEffect, useState } from "react";
 import { useLocation, useParams } from "react-router-dom";
-import { useMediaQuery } from 'react-responsive';
+import { useMediaQuery } from "react-responsive";
 import { Header } from "../components/Header";
 import { InstanceView } from "../components/InstanceView";
 import { TemplateDetails } from "../components/TemplateDetails";
-import { Button } from '../components/Button';
+import { Button } from "../components/Button";
 
 export const TemplateView = ({ endpoint }) => {
-    let { templateName, objectID } = useParams();
-    const [singleObjectID, setObjectID] = useState(null);
-    const location = useLocation();
-    const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
-    const isSmallScreen = useMediaQuery({ query: '(max-width: 1024px)' });
+  let { templateName, objectID } = useParams();
+  const [singleObjectID, setObjectID] = useState(null);
+  const location = useLocation();
+  const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
+  const isSmallScreen = useMediaQuery({ query: "(max-width: 1024px)" });
 
+  useEffect(() => {
+    if (isSmallScreen) {
+      setIsSidebarCollapsed(true);
+    }
+  }, [isSmallScreen]);
 
-    useEffect(() => {}, [endpoint, templateName, objectID, location]);
-
-    return (
-        <div className="flex flex-col h-screen">
-            <Header />
-            {isSmallScreen && (
-                <div className="flex justify-center">
-                    <Button 
-                        className="px-4 py-2"
-                        onClick={() => setIsSidebarCollapsed(!isSidebarCollapsed)}
-                    >
-                        {isSidebarCollapsed ? 'Expand Sidebar' : 'Collapse Sidebar'}
-                    </Button>
-                </div>
-            )}
-            <div className="flex flex-1 overflow-hidden">
-                <aside className={`print:hidden lg:block lg:w-80 h-full p-4 overflow-y-auto ${isSidebarCollapsed ? 'hidden' : 'w-64 h-screen'}`}>
-                    <TemplateDetails
-                        endpoint={endpoint}
-                        onSingleInstance={setObjectID}
-                    />
-                </aside>            
-                <main className="flex-1 overflow-hidden p-4">
-                    <div className="html-wrapper w-full p-4 max-w-none lg:max-w-4xl min-w-0 lg:min-w-[850px] overflow-y-hidden h-full flex items-center justify-center">
-                        {!!!objectID && !!!singleObjectID && (
-                            <p className="text-xl text-gray-700">
-                                Select an Instance
-                            </p>
-                        )}
-                        {(objectID || singleObjectID) && (
-                            <InstanceView
-                                endpoint={endpoint}
-                                objectID={objectID || singleObjectID}
-                                templateName={templateName}
-                            />
-                        )}
-                    </div>
-                </main>
-            </div>
+  return (
+    <div className="flex h-screen w-auto flex-col">
+      <Header />
+      {isSmallScreen && (
+        <div className="mt-8 flex justify-center">
+          <Button
+            className="px-4 py-2"
+            onClick={() => setIsSidebarCollapsed(!isSidebarCollapsed)}
+          >
+            {isSidebarCollapsed ? "Expand Sidebar" : "Collapse Sidebar"}
+          </Button>
         </div>
-    );
+      )}
+      <div className="flex-32 flex">
+        <aside
+          className={`mb-8 mt-8 flex max-h-[75vh] flex-col overflow-y-auto rounded-lg shadow-lg dark:shadow-dark lg:block lg:w-96 print:hidden ${
+            isSidebarCollapsed ? "hidden" : "h-screen w-64"
+          }`}
+        >
+          <TemplateDetails endpoint={endpoint} onSingleInstance={setObjectID} />
+        </aside>
+        <main className="flex-1 ">
+          <div className="html-wrapper ml-8 mt-8 flex h-[80vh] min-w-0 max-w-none items-start justify-center  lg:min-w-[650px] lg:max-w-4xl">
+            {!!!objectID && !!!singleObjectID && (
+              <p className="text-xl text-gray-700 dark:text-gray-300">
+                Select an Instance
+              </p>
+            )}
+            {(objectID || singleObjectID) && (
+              <InstanceView
+                endpoint={endpoint}
+                objectID={objectID || singleObjectID}
+                templateName={templateName}
+              />
+            )}
+          </div>
+        </main>
+      </div>
+    </div>
+  );
 };
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 03e0f82..70e55ea 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -6,18 +6,30 @@
 /** @type {import('tailwindcss').Config} */
 export default {
   darkMode: "class",
-  content: [
-    './src/**/*.{html,js,jsx}'
-  ],
+  content: ["./src/**/*.{html,js,jsx}"],
   theme: {
     extend: {
       boxShadow: {
-        white: '0 0 15px rgba(255, 255, 255, 0.1)',
+        white: "0 0 15px rgba(255, 255, 255, 0.1)",
+        dark: "0 4px 6px 0 rgba(0, 0, 0, 0.2)",
       },
       animation: {
-        'spin-slow': 'spin 1.6s linear infinite',
-      }
-    }
+        "spin-slow": "spin 1.6s linear infinite",
+      },
+      opacity: {
+        54: ".54",
+      },
+      colors: {
+        "custom-light": "#dee4e7",
+        "custom-dark-1": "#24273a",
+        "custom-dark-2": "#363a4f",
+        "custom-dark-3": "#6e738d",
+        "custom-dark-4": "#5b6078",
+        "custom-dark-error": "#ed8796",
+        "custom-blue": "#2196f3",
+        "custom-blue-hover": "#1976d2",
+      },
+    },
   },
-  plugins: [],
-}
+  plugins: [require("tailwind-scrollbar")],
+};
diff --git a/templates/__generic__.html.j2 b/templates/__generic__.html.j2
index 10f147d..77be0fa 100644
--- a/templates/__generic__.html.j2
+++ b/templates/__generic__.html.j2
@@ -11,4 +11,4 @@
 <h1>Unnamed ({{ object.__class__.__name__ }})</small></h1>
 {% endif %}
 
-{{show_other_attributes(object) | safe}}
\ No newline at end of file
+{{show_other_attributes(object) | safe}}
diff --git a/templates/classes.html.j2 b/templates/classes.html.j2
index 656b446..76ee6fc 100644
--- a/templates/classes.html.j2
+++ b/templates/classes.html.j2
@@ -94,4 +94,3 @@
 
 <h2>Other Object Attributes</h2>
 {{ show_other_attributes(object, handled_attrs) | safe }}
-
diff --git a/templates/common_macros.html.j2 b/templates/common_macros.html.j2
index 3185f04..99a0b55 100644
--- a/templates/common_macros.html.j2
+++ b/templates/common_macros.html.j2
@@ -74,4 +74,4 @@
 
 {%- macro typed_name(object) -%}<b>{{ object.__class__.__name__ }}</b> <a href="{{ object | make_href }}">{{ object.name  | trim }}</a>{%- endmacro -%}
 
-{%- macro linked_name(object) -%}<a href="{{ object | make_href }}">{{ object.name  | trim }}</a>{%- endmacro -%}
\ No newline at end of file
+{%- macro linked_name(object) -%}<a href="{{ object | make_href }}">{{ object.name  | trim }}</a>{%- endmacro -%}
diff --git a/templates/operational-activity.html.j2 b/templates/operational-activity.html.j2
index b97f455..e029286 100644
--- a/templates/operational-activity.html.j2
+++ b/templates/operational-activity.html.j2
@@ -8,12 +8,12 @@
 {{ description(object) | safe }}
 
 {% if object.owner %}
-    <p>This activity is performed by 
-        <b>{{ object.owner.__class__.__name__}}</b> 
+    <p>This activity is performed by
+        <b>{{ object.owner.__class__.__name__}}</b>
         <a href="{{ object.owner | make_href }}">{{ object.owner.name }}</a>.
     </p>
     {% if object.available_in_states %}
-    <p><a href="{{ object.owner | make_href}}">{{ object.owner.name }}</a> may {{ object.name }} while in one of the following modes or states:&nbsp; 
+    <p><a href="{{ object.owner | make_href}}">{{ object.owner.name }}</a> may {{ object.name }} while in one of the following modes or states:&nbsp;
     {%- for state in object.available_in_states -%}
         <a href="{{ state | make_href }}">{{ state.name }}</a>{%- if not loop.last -%}, {% endif -%}
     {%- endfor -%}
@@ -21,7 +21,7 @@
     {% endif %}
 
     <h2>Interface partners</h2>
-    <p>While performing this activity <a href="{{ object.owner | make_href}}">{{ object.owner.name }}</a> may interact with the following entities:&nbsp; 
+    <p>While performing this activity <a href="{{ object.owner | make_href}}">{{ object.owner.name }}</a> may interact with the following entities:&nbsp;
     {% set ins = object.inputs | map(attribute="source") | selectattr("owner") | map(attribute="owner") | list %}
     {% set outs = object.outputs | map(attribute="target") | selectattr("owner") | map(attribute="owner") | list %}
     {% set partners = ins + outs | rejectattr("uuid", "equalto", object.owner.uuid) | list %}
@@ -38,12 +38,12 @@
         {% for input in object.inputs %}
             <li>
                     {%- set act = input.source -%}
-                    {{ linked_name(input) | safe }} produced by "{{ linked_name(act) | safe }}" activity of&nbsp; 
+                    {{ linked_name(input) | safe }} produced by "{{ linked_name(act) | safe }}" activity of&nbsp;
                     {%- if act.owner -%}
-                        {{ linked_name(act.owner) | safe }} 
+                        {{ linked_name(act.owner) | safe }}
                     {%- else -%}
-                        <span style="color: red;">Unassigned entity</span> 
-                    {%- endif -%} 
+                        <span style="color: red;">Unassigned entity</span>
+                    {%- endif -%}
             </li>
         {% endfor %}
     </ul>
@@ -57,12 +57,12 @@
 <li>
     {%- set act = output.target -%}
     {%- if act.owner -%}
-        {{ linked_name(act.owner) | safe }} 
+        {{ linked_name(act.owner) | safe }}
     {%- else -%}
-        <span style="color: red;">Unassigned entity</span> 
+        <span style="color: red;">Unassigned entity</span>
     {%- endif -%}&nbsp;
     may require {{ linked_name(output) | safe }} to perform "{{ linked_name(act) | safe }}" activity.
-    
+
 </li>
 {% endfor %}
 </ul>
@@ -78,4 +78,4 @@ The figure below provides an overview of the interactions between the activity o
 
 <h2>Other properties of "{{ object.name }}"</h2>
 {% set excluded = ["name", "available_in_states", "related_exchanges", "inputs", "outputs", "context_diagram", "xtype", "parent", "owner", "owning_entity"] %}
-{{ show_other_attributes(object, excluded) | safe  }}
\ No newline at end of file
+{{ show_other_attributes(object, excluded) | safe  }}
diff --git a/templates/operational-capability.html.j2 b/templates/operational-capability.html.j2
index 47225db..9dbd011 100644
--- a/templates/operational-capability.html.j2
+++ b/templates/operational-capability.html.j2
@@ -1,3 +1,7 @@
+{#
+    Copyright DB InfraGO AG and contributors
+    SPDX-License-Identifier: Apache-2.0
+#}
 {% from 'common_macros.html.j2' import show_other_attributes, description, render_reqs_by_type %}
 
 <h1>{{ object.name }}</h1>
@@ -40,4 +44,3 @@
 
 {% set filtered = ["description", "xtype", "postcondition", "precondition", "context_diagram", "data_flow_view", "diagrams"]%}
 {{ show_other_attributes(object, filtered) | safe}}
-
diff --git a/templates/operational-entity.html.j2 b/templates/operational-entity.html.j2
index fa1c131..c86360f 100644
--- a/templates/operational-entity.html.j2
+++ b/templates/operational-entity.html.j2
@@ -1,3 +1,7 @@
+{#
+    Copyright DB InfraGO AG and contributors
+    SPDX-License-Identifier: Apache-2.0
+#}
 {% from 'common_macros.html.j2' import show_other_attributes, description, render_reqs_by_type, typed_name %}
 
 <h1>{{ object.name }}</h1>
@@ -6,9 +10,9 @@
 <p style="margin-top:1.5em; "><b>Parent:</b> {{ typed_name(object.parent) | safe }}</p>
 
 {% if object.capabilities %}
-<p><i>{{ object.name }} {% if object.is_human %}(human){% endif %}</i> is involved in the following operational capabilities: 
+<p><i>{{ object.name }} {% if object.is_human %}(human){% endif %}</i> is involved in the following operational capabilities:
 {%- for cap in object.capabilities -%}
-<a href="{{ cap | make_href }}">{{ cap.name }}</a>; 
+<a href="{{ cap | make_href }}">{{ cap.name }}</a>;
 {%- endfor -%}
 </p>
 {% endif %}
@@ -52,10 +56,10 @@
             {%- endif -%}
         {%- endfor -%}
         {%- if involving_caps -%}
-            <p>involving operational capabilities: 
+            <p>involving operational capabilities:
             {%- for cap in object.capabilities -%}
                 {%- if activity in cap.involved_activities -%}
-                <a href="{{ cap | make_href }}">{{ cap.name }}</a>; 
+                <a href="{{ cap | make_href }}">{{ cap.name }}</a>;
                 {%- endif -%}
             {%- endfor -%}
             </p>
diff --git a/templates/req.html.j2 b/templates/req.html.j2
index d99366b..0151afa 100644
--- a/templates/req.html.j2
+++ b/templates/req.html.j2
@@ -27,4 +27,4 @@
 {% from 'common_macros.html.j2' import show_other_attributes %}
 
 {% set filtered = ["parent", "owner", "requirements", "long_name", "to_reqif"]%}
-{{show_other_attributes(object, filtered) | safe}}
\ No newline at end of file
+{{show_other_attributes(object, filtered) | safe}}
diff --git a/templates/req.yaml b/templates/req.yaml
index ba35e61..c20eeab 100644
--- a/templates/req.yaml
+++ b/templates/req.yaml
@@ -8,4 +8,4 @@ description: Basic template to visualize requirement objects
 category: other
 variable:
   name: object
-  type: CapellaModule
\ No newline at end of file
+  type: CapellaModule
diff --git a/templates/system-capability.html.j2 b/templates/system-capability.html.j2
index b0626d1..712f741 100644
--- a/templates/system-capability.html.j2
+++ b/templates/system-capability.html.j2
@@ -59,4 +59,4 @@
 <h2>Other properties of "{{ object.name }}"</h2>
 
 {% set excluded = ["involved_functions", "postcondition", "precondition", "description", "name", "data_flow_view", "context_diagram", "xtype", "parent"] %}
-{{ show_other_attributes(object, excluded) | safe  }}
\ No newline at end of file
+{{ show_other_attributes(object, excluded) | safe  }}
diff --git a/templates/system_function.html.j2 b/templates/system_function.html.j2
index 22e6a89..2493e0b 100644
--- a/templates/system_function.html.j2
+++ b/templates/system_function.html.j2
@@ -39,4 +39,4 @@
 
 <h2>Other properties of "{{ object.name }}"</h2>
 {% set excluded = ["context_diagram", "description", "name", "owner", "xtype"] %}
-{{ show_other_attributes(object, excluded) | safe}}
\ No newline at end of file
+{{ show_other_attributes(object, excluded) | safe}}