diff --git a/frontend/src/components/project-card.njk b/frontend/src/components/project-card.njk
new file mode 100644
index 0000000..ab107f6
--- /dev/null
+++ b/frontend/src/components/project-card.njk
@@ -0,0 +1,64 @@
+
+
+
+
+
+
![]()
+
+
+
+
Title
+
+
+
+
+
+
diff --git a/frontend/src/components/project-card.ts b/frontend/src/components/project-card.ts
new file mode 100644
index 0000000..1aa68d9
--- /dev/null
+++ b/frontend/src/components/project-card.ts
@@ -0,0 +1,36 @@
+import {TesoftComponent} from "../scripts/main.ts";
+
+export class TesoftProjectCard extends TesoftComponent {
+ static observedAttributes = ["href", "logo-src", "title-text"];
+
+ private readonly anchor: HTMLAnchorElement;
+ private readonly logoImage: HTMLImageElement;
+ private readonly heading: HTMLHeadingElement;
+
+ constructor() {
+ super();
+
+ const template = document.getElementById("tesoft-project-card-template") as HTMLTemplateElement;
+ const templateContent = template.content;
+
+ const shadowRoot = this.attachShadow({mode: "open"});
+ shadowRoot.appendChild(templateContent.cloneNode(true));
+
+ this.anchor = shadowRoot.querySelector("a")!;
+ this.logoImage = shadowRoot.querySelector("img")!;
+ this.heading = shadowRoot.querySelector("h4")!;
+ }
+
+ // noinspection JSUnusedGlobalSymbols
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
+ if (name === "href") {
+ this.anchor.href = newValue ?? "";
+ } else if (name === "logo-src") {
+ this.logoImage.src = newValue ?? "";
+ } else if (name === "title-text") {
+ this.heading.textContent = newValue ?? "";
+ }
+ }
+}
+
+customElements.define("tesoft-project-card", TesoftProjectCard);
diff --git a/frontend/src/pages/index.njk b/frontend/src/pages/index.njk
index 4eba07b..8f7fc6c 100644
--- a/frontend/src/pages/index.njk
+++ b/frontend/src/pages/index.njk
@@ -11,7 +11,32 @@
{% endblock %}
{% block components %}
+ {% includeOnce 'components/project-card.njk' %}
{% endblock %}
{% block content %}
+ Welcome
+ Welcome to my website. On this you can find my projects, a blog and information about me.
+
+ Current projects
+
+
+ Source code of this website programmed in Go and Typescript.
+ Blog articles are written in Markdown, parsed by
+ goldmark and stored
+ in an SQLite database via
+ go-sqlite3.
+
+
+ Website of a small bar and event location.
+ The frontend is written in a combination of web components and Nunjucks to have proper file separation.
+ The backend uses Directus and a couple of self-made extensions to fit the needs of the operators.
+
+
+ A calculator for rental service charges written in C# with WPF. The interface language is German only.
+
+
{% endblock %}
diff --git a/frontend/src/styles/index.css b/frontend/src/styles/index.css
index e69de29..43885f7 100644
--- a/frontend/src/styles/index.css
+++ b/frontend/src/styles/index.css
@@ -0,0 +1,10 @@
+h4 {
+ padding-top: var(--large-padding);
+}
+
+.projects {
+ display: grid;
+ gap: var(--medium-padding);
+ grid-template-columns: 1fr;
+ grid-auto-rows: auto;
+}