diff --git a/package-lock.json b/package-lock.json
index 93afde7f3c7f1d76bcb9645cdcb372c1ba9813ef..7ce95d5d58bab84e47ba52b3b9632398557c2b50 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,7 +13,7 @@
         "vue-router": "^4.1.5"
       },
       "devDependencies": {
-        "@inkline/inkline": "^3.2.0",
+        "@inkline/inkline": "^3.2.2",
         "@intlify/unplugin-vue-i18n": "^0.8.1",
         "@vitejs/plugin-vue": "^4.0.0",
         "eslint": "8.22.0",
@@ -387,9 +387,9 @@
       "dev": true
     },
     "node_modules/@inkline/inkline": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/@inkline/inkline/-/inkline-3.2.0.tgz",
-      "integrity": "sha512-h14lFCtcD0A80LtpAA+NF27CCC+wfmdxK9Q/No05Cqx6a4tnQFAVjYVtH6OktC/ZEAAn1gjjl7S0RQdXJz03YQ==",
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/@inkline/inkline/-/inkline-3.2.2.tgz",
+      "integrity": "sha512-qwMmoN9YaqfEtTx/PRbRwmg8W2ky2iWQuEb34ZcKh1vSff4HWg512EyCQwzO3Jv9KzG0x4lL32uYchIhM5YFzg==",
       "dev": true,
       "dependencies": {
         "@popperjs/core": "2.11.6"
@@ -3622,9 +3622,9 @@
       "dev": true
     },
     "@inkline/inkline": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/@inkline/inkline/-/inkline-3.2.0.tgz",
-      "integrity": "sha512-h14lFCtcD0A80LtpAA+NF27CCC+wfmdxK9Q/No05Cqx6a4tnQFAVjYVtH6OktC/ZEAAn1gjjl7S0RQdXJz03YQ==",
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/@inkline/inkline/-/inkline-3.2.2.tgz",
+      "integrity": "sha512-qwMmoN9YaqfEtTx/PRbRwmg8W2ky2iWQuEb34ZcKh1vSff4HWg512EyCQwzO3Jv9KzG0x4lL32uYchIhM5YFzg==",
       "dev": true,
       "requires": {
         "@popperjs/core": "2.11.6"
diff --git a/package.json b/package.json
index 7c8179e5d13f24679b92ebda666e2b46ff6ee12c..be2bedddb70d1d11011c5f613f61353a80e30aa6 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
     "vue-router": "^4.1.5"
   },
   "devDependencies": {
-    "@inkline/inkline": "^3.2.0",
+    "@inkline/inkline": "^3.2.2",
     "@intlify/unplugin-vue-i18n": "^0.8.1",
     "@vitejs/plugin-vue": "^4.0.0",
     "eslint": "8.22.0",
diff --git a/src/assets/app.scss b/src/assets/app.scss
index 2cee20b0569763337f4235319041052d5c282b07..959d6d3a9b37fa7e4849876ba11ba3c866f060b2 100644
--- a/src/assets/app.scss
+++ b/src/assets/app.scss
@@ -10,6 +10,19 @@
   --contrast-color-for-light-background: var(--color--gray-75);
   --contrast-color-for-dark-background: var(--color--gray-10);
   --body--color: var(--color--gray-75);
+  --h3--font-size: 1.4rem;
+  --color--negative-background: var(--color--red-20);
+  --color--negative-text: var(--color--red-65);
+  --color--negative-text-dark: var(--color--red-25);
+  --color--negative-background-dark: hsl(var(--color--red-70-h), 40%, var(--color--red-60-l));
+  --color--medium-background: var(--color--yellow-20);
+  --color--medium-text: var(--color--yellow-70);
+  --color--medium-text-dark: var(--color--yellow-50);
+  --color--medium-background-dark: hsl(var(--color--yellow-70-h), 60%, var(--color--yellow-70-l));
+  --color--positive-background: var(--color--green-20);
+  --color--positive-text: var(--color--green-50);
+  --color--positive-text-dark: var(--color--green-50);
+  --color--positive-background-dark: hsl(var(--color--green-70-h), 50%, var(--color--green-60-l));
 }
 
 h3 {
@@ -21,28 +34,45 @@ h3 {
 }
 
 .eval-negative {
-  background-color: var(--color--red-20);
-
+  background-color: var(--color--negative-background);
   .-dark & {
-    background-color: hsl(var(--color--red-70-h), 40%, var(--color--red-60-l));
+    background-color: var(--color--negative-background-dark);
   }
 
 }
 
 .eval-medium {
-  background-color: var(--color--yellow-20);
-
+  background-color: var(--color--medium-background);
   .-dark & {
-    background-color: hsl(var(--color--yellow-70-h), 60%, var(--color--yellow-70-l));
+    background-color: var(--color--negative-dark-background);
   }
 }
 
 .eval-positive {
-  background-color: var(--color--green-20);
+  background-color: var(--color--positive-background);
+  .-dark & {
+    background-color: var(--color--positive-dark-background);
+  }
+}
 
+.text-negative {
+  color: var(--color--negative-text);
   .-dark & {
-    background-color: hsl(var(--color--green-70-h), 50%, var(--color--green-60-l));
+    color: var(--color--negative-text-dark);
+  }
+}
+
+.text-medium {
+  color: var(--color--medium-text);
+  .-dark & {
+    color: var(--color--medium-text-dark);
+  }
+}
 
+.text-positive {
+  color: var(--color--positive-text);
+  .-dark & {
+    color: var(--color--positive-text-dark);
   }
 }
 
@@ -101,3 +131,24 @@ h3 {
     }
   }
 }
+
+.popover-wrapper {
+  ----footer--background: var(----body--background) !important;
+  .popover-body, .popover-footer, .popover-header {
+    padding-left: 12px;
+    padding-right: 12px;
+  }
+}
+
+.card {
+  .card-header {
+    padding-left: 16px;
+    padding-right: 0px;
+  }
+  .card-body {
+    padding-left: 16px;
+    padding-right: 16px;
+    min-height: 120px;
+  }
+}
+
diff --git a/src/components/Projects.vue b/src/components/Projects.vue
index 98c948358685999f2af9467c17d5d1e5a27bb24a..1a2ac887edcfc51f0baf38fc1e4880794f290680 100644
--- a/src/components/Projects.vue
+++ b/src/components/Projects.vue
@@ -1,10 +1,69 @@
 <template>
-  <p>{{$t('no_content')}}</p>
+  <template v-if="releases.length === 0">
+    <p>{{$t('no_content')}}</p>
+  </template>
+  <template v-else>
+    <div
+      v-for="{ tag, projects } in releases"
+      :key="tag"
+      class="_border-left _border-left-color:gray-40 _padding-bottom:5"
+    >
+      <div class="timeline-item _font-weight:bold _padding-left:3">
+        <i-badge size="lg" class="_padding:1 _font-weight:bold">ocrd_all {{tag}}</i-badge>
+      </div>
+      <i-row class="projects-container _margin-left:2 _margin-top:3 _display:flex">
+        <i-column v-for="projectId in projects" :key="projectId" sm="3" class="_margin-bottom:2 _display:flex">
+          <Project :id="projectId" class="_flex-grow:1"></Project>
+        </i-column>
+      </i-row>
+    </div>
+  </template>
 </template>
 
 <script setup>
+
+import { onMounted, ref } from "vue";
+import { store } from "@/helpers/store";
+import api from "@/helpers/api";
+import Project from "@/components/projects/Project.vue";
+
+const releases = ref([]);
+
+onMounted(async () => {
+  store.setRepos(await api.getProjects());
+
+  releases.value = await api.getOcrdAllReleases();
+  store.setReleases(releases.value);
+});
 </script>
 
-<style scoped>
+<style lang="scss" scoped>
+.timeline-item {
+  &:before {
+    content: "";
+    position: absolute;
+    display: block;
+    width: 18px;
+    height: 18px;
+    background: var(--color--gray-40);
+    left: -10px;
+    top: 50%;
+    transform: translateY(-50%);
+    border-radius: 50%;
+  }
+}
 
+.badge {
+  &:before {
+    content: "";
+    position: absolute;
+    display: block;
+    width: 8px;
+    height: 8px;
+    background: var(----background);
+    left: -4px;
+    top: 50%;
+    transform: translateY(-50%) rotate(45deg);
+  }
+}
 </style>
diff --git a/src/components/projects/Project.vue b/src/components/projects/Project.vue
new file mode 100644
index 0000000000000000000000000000000000000000..7f89adcdc644882e4d0aed74da6f2e881423d7e2
--- /dev/null
+++ b/src/components/projects/Project.vue
@@ -0,0 +1,102 @@
+<template>
+  <i-card>
+      <template #header>
+        <div class="_display:flex">
+          <h3 class="card-title _margin-top:0">{{ props.id }}</h3>
+          <div class="_margin-left:auto">
+            <i-popover placement="top">
+              <i-button link color="dark">
+                <i-icon name="ink-info"></i-icon>
+              </i-button>
+              <template #header>
+                <h5 class="_margin-top:0">{{ $t('additional_info')}}</h5>
+              </template>
+              <template #body>
+                <p>This is the popover body. Useful information goes here.</p>
+              </template>
+              <template #footer>
+                <div class="_display:flex">
+                  <a :href="readmeUrl" title="README" target="_blank">README</a>
+                  <a :href="changeLogUrl" title="CHANGELOG" target="_blank" class="_margin-left:auto">CHANGELOG</a>
+                </div>
+              </template>
+          </i-popover>
+          </div>
+          </div>
+      </template>
+      <div v-if="repo">
+        <div v-for="{ icon, label } in statusList" :key="label" class="_display:flex _align-items:center">
+          <Icon
+              :name="icon"
+              class="_margin-right:1"
+              :class="{
+                'text-negative': icon === 'slash',
+                'text-medium': icon === 'alert-triangle',
+                'text-positive': icon === 'check-circle'
+              }">
+
+          </Icon>
+          <span>{{ label }}</span>
+        </div>
+        <div class="_display:flex _margin-top:2 _margin-bottom:1">
+          <i-badge v-if="projectType">{{ projectType }}</i-badge>
+        </div>
+      </div>
+      <div v-else class="_display:flex _height:100% _align-items:center _justify-content:center">
+        <span>{{ $t('error_repo_not_found') }}</span>
+      </div>
+  </i-card>
+</template>
+
+<script setup>
+import { computed, ref, watch } from "vue";
+import { store } from "@/helpers/store";
+import Icon from "@/components/Icon.vue";
+import { useI18n } from "vue-i18n";
+
+
+const { t } = useI18n();
+
+const props = defineProps(['id']);
+const repo = ref(null);
+
+watch(() => props.id, (id) => {
+  repo.value = store.getRepoById(id);
+}, { immediate: true });
+
+const statusList = computed(() => {
+  return repo.value ? [
+      ...(repo.value.unreleased_changes
+          ? [{ icon: 'alert-triangle', label: repo.value.unreleased_changes + ' ' + t('unreleased_changes') }]
+          : [{ icon: 'check-circle', label: t('version') + ' ' + repo.value.latest_version }]
+      ),
+      ...(repo.value.ocrd_tool_json_valid
+          ? [{ icon: 'check-circle', label: 'ocrd-tool.json ' + t('is_valid') }]
+          : [{ icon: 'slash', label: 'ocrd-tool.json ' + t('is_not_valid') }]
+      ),
+    ...(repo.value.dependency_conflicts
+            ? [{ icon: 'alert-triangle', label: t('dependency_conflicts') }]
+            : [{ icon: 'check-circle', label: t('no_dependency_conflicts') }]
+    )
+  ] : [];
+});
+
+const readmeUrl = computed(() => {
+  if (!repo.value) return '';
+  return repo.value.additional_info.links['README.md'];
+});
+
+const changeLogUrl = computed(() => {
+  if (!repo.value) return '';
+  return repo.value.additional_info.links['README.md'];
+});
+
+const projectType = computed(() => {
+  if (!repo.value) return null;
+  return repo.value.project_type;
+});
+
+</script>
+
+<style lang="scss" scoped>
+</style>
diff --git a/src/helpers/api.js b/src/helpers/api.js
index 650058af0e36fa427f041bdee145a3183ef941f4..e0e9d482a2c7b461735a23c3df8cefdd4f91f16c 100644
--- a/src/helpers/api.js
+++ b/src/helpers/api.js
@@ -4,6 +4,10 @@ async function getProjects() {
     return await request(baseUrl + '/repos.json');
 }
 
+async function getOcrdAllReleases() {
+    return await request(baseUrl + '/ocrd_all_releases.json');
+}
+
 async function getWorkflows() {
     return await request(baseUrl + '/workflows.json');
     // return Promise.resolve(workflowsJson);
@@ -21,5 +25,6 @@ async function request (url) {
 export default {
     getProjects,
     getWorkflows,
-    getEvalDefinitions
+    getEvalDefinitions,
+    getOcrdAllReleases
 };
diff --git a/src/helpers/store.js b/src/helpers/store.js
index 6d1f1796a41579429fafea1182b5ef865e77d0eb..fb5284668849eeed27fba80002a01763bb9825c6 100644
--- a/src/helpers/store.js
+++ b/src/helpers/store.js
@@ -2,15 +2,24 @@ import { reactive } from 'vue';
 
 export const store = reactive({
   repos: [],
+  releases: [],
   evaluations: [],
   metricDefinitions: {},
   setRepos(repos) {
     this.repos = repos;
   },
+  setReleases(releases) {
+    this.releases = releases;
+  },
   setEvaluations(evaluations) {
     this.evaluations = evaluations;
   },
   setMetricDefinitions(defs) {
     this.metricDefinitions = defs;
+  },
+  getRepoById(id) {
+    return this.repos.find(repo => {
+      return repo.id === id;
+    });
   }
 });
diff --git a/src/locales/de.json b/src/locales/de.json
index 931e5e1573c5a43d44b10b8aae5c584598a09ede..5534205e0bf5481aa040383ea1094ef167123c37 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -35,5 +35,13 @@
   "select_all": "Alle auswählen",
   "document": "Dokument",
   "workflow": "Workflow",
-  "fastest": "Am schnellsten"
+  "fastest": "Am schnellsten",
+  "unreleased_changes": "unveröffentliche Änderungen",
+  "version": "Version",
+  "is_valid": "ist gültig",
+  "is_not_valid": "ist ungültig",
+  "dependency_conflicts": "Konflikte mit Abhängigkeiten",
+  "no_dependency_conflicts": "Keine Konflikte mit Abhängigkeiten",
+  "additional_info": "Zusätzliche Info",
+  "error_repo_not_found": "Keine Daten zum Repository gefunden."
 }
diff --git a/src/locales/en.json b/src/locales/en.json
index 1fc303ee955c0ca85455885c782b6e6945685473..f221408ecb07de2688119f8909ba31933d18b4ab 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -29,5 +29,13 @@
   "select_all": "Select all",
   "document": "Document",
   "workflow": "Workflow",
-  "fastest": "Fastest"
+  "fastest": "Fastest",
+  "unreleased_changes": "unreleased changes",
+  "version": "Version",
+  "is_valid": "is valid",
+  "is_not_valid": "is not valid",
+  "dependency_conflicts": "Dependency conflicts",
+  "no_dependency_conflicts": "No dependency conflicts",
+  "additional_info": "Additional Info",
+  "error_repo_not_found": "No repository data found."
 }
diff --git a/src/main.js b/src/main.js
index 2d0d30f82d7848923ea95561cec79593d18ecd02..90f9999a650e88e9d7c128447a19fd673775f361 100644
--- a/src/main.js
+++ b/src/main.js
@@ -12,7 +12,7 @@ import en from './locales/en.json';
 import de from './locales/de.json';
 
 const i18n = createI18n({
-  locale: 'de',
+  locale: 'en',
   messages: { en, de }
 });
 
diff --git a/src/router/index.js b/src/router/index.js
index f1b850f533a96e78e44c1c21b9e0fe7adaf7accc..ac774950b4d7b3ec1f4c22fff56d805b572819f2 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,19 +1,19 @@
-import { createRouter, createWebHashHistory } from 'vue-router';
+import { createRouter, createWebHistory } from 'vue-router';
 import HomeView from '../views/HomeView.vue';
 import Workflows from "@/components/Workflows.vue";
 import Projects from "@/components/Projects.vue";
 import Processors from "@/components/Processors.vue";
 
 const router = createRouter({
-  history: createWebHashHistory(import.meta.env.BASE_URL),
+  history: createWebHistory(import.meta.env.BASE_URL),
   routes: [
     {
       path: '/',
       name: 'home',
       component: HomeView,
       children: [
-        { path: 'projects', name: 'Projects', component: Projects },
-        { path: 'processors', name: 'Processors', component: Processors },
+        { path: 'projects', name: 'projects', component: Projects },
+        { path: 'processors', name: 'processors', component: Processors },
         { path: 'workflows', alias: '/', name: 'workflows', component: Workflows },
       ],
     },
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index 7b5c88b25133ca6a3340087c2d76f42f2b67e5c1..cfe9deb9fb815874a47a3d671b254c2427d80853 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -24,12 +24,14 @@
 </template>
 <script setup>
 import { onMounted, ref, watch, inject } from "vue";
-import { useRouter } from "vue-router";
+import { useRoute, useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
 
 import { getIcon } from '@/helpers/icon';
 
 const router = useRouter();
+const route = useRoute();
+
 const { t } = useI18n();
 const activeTab = ref('/workflows');
 
@@ -57,6 +59,7 @@ const items = ref([
 
 onMounted(async () => {
   await router.isReady();
+  activeTab.value = '/' + route.name;
 });
 
 const inkline = inject('inkline', {});