From 8e1312e4f0c57fc57a16adf23e27ea15ff183c7d Mon Sep 17 00:00:00 2001
From: OZGCloud <ozgcloud@mgm-tp.com>
Date: Mon, 4 Nov 2024 15:54:42 +0100
Subject: [PATCH] OZG-7021 add info app, add tailwind typography

---
 alfa-client/apps/info-e2e/.eslintrc.json      |  10 ++
 alfa-client/apps/info-e2e/cypress.config.ts   |   7 ++
 alfa-client/apps/info-e2e/project.json        |  29 +++++
 alfa-client/apps/info-e2e/src/e2e/app.cy.ts   |  13 ++
 .../apps/info-e2e/src/fixtures/example.json   |   5 +
 .../apps/info-e2e/src/support/app.po.ts       |   1 +
 .../apps/info-e2e/src/support/commands.ts     |  35 ++++++
 alfa-client/apps/info-e2e/src/support/e2e.ts  |  17 +++
 alfa-client/apps/info-e2e/tsconfig.json       |  17 +++
 alfa-client/apps/info/.eslintrc.json          |  33 +++++
 alfa-client/apps/info/jest.config.ts          |  22 ++++
 alfa-client/apps/info/project.json            |  95 +++++++++++++++
 .../apps/info/src/app/app.component.html      | 115 ++++++++++++++++++
 .../apps/info/src/app/app.component.scss      |   0
 .../apps/info/src/app/app.component.spec.ts   |  25 ++++
 .../apps/info/src/app/app.component.ts        |  13 ++
 alfa-client/apps/info/src/app/app.config.ts   |   7 ++
 alfa-client/apps/info/src/app/app.routes.ts   |   3 +
 alfa-client/apps/info/src/assets/.gitkeep     |   0
 alfa-client/apps/info/src/favicon.ico         | Bin 0 -> 15086 bytes
 alfa-client/apps/info/src/index.html          |  13 ++
 alfa-client/apps/info/src/main.ts             |   5 +
 alfa-client/apps/info/src/styles.scss         |  11 ++
 alfa-client/apps/info/src/test-setup.ts       |   8 ++
 alfa-client/apps/info/tailwind.config.js      |  16 +++
 alfa-client/apps/info/tsconfig.app.json       |  10 ++
 alfa-client/apps/info/tsconfig.editor.json    |   6 +
 alfa-client/apps/info/tsconfig.json           |  32 +++++
 alfa-client/apps/info/tsconfig.spec.json      |  11 ++
 .../src/lib/tailwind-preset/root.css          |   7 ++
 .../lib/tailwind-preset/tailwind.config.js    |   2 +-
 alfa-client/nx.json                           |  11 +-
 alfa-client/package-lock.json                 |  45 +++++++
 alfa-client/package.json                      |   2 +
 34 files changed, 622 insertions(+), 4 deletions(-)
 create mode 100644 alfa-client/apps/info-e2e/.eslintrc.json
 create mode 100644 alfa-client/apps/info-e2e/cypress.config.ts
 create mode 100644 alfa-client/apps/info-e2e/project.json
 create mode 100644 alfa-client/apps/info-e2e/src/e2e/app.cy.ts
 create mode 100644 alfa-client/apps/info-e2e/src/fixtures/example.json
 create mode 100644 alfa-client/apps/info-e2e/src/support/app.po.ts
 create mode 100644 alfa-client/apps/info-e2e/src/support/commands.ts
 create mode 100644 alfa-client/apps/info-e2e/src/support/e2e.ts
 create mode 100644 alfa-client/apps/info-e2e/tsconfig.json
 create mode 100644 alfa-client/apps/info/.eslintrc.json
 create mode 100644 alfa-client/apps/info/jest.config.ts
 create mode 100644 alfa-client/apps/info/project.json
 create mode 100644 alfa-client/apps/info/src/app/app.component.html
 create mode 100644 alfa-client/apps/info/src/app/app.component.scss
 create mode 100644 alfa-client/apps/info/src/app/app.component.spec.ts
 create mode 100644 alfa-client/apps/info/src/app/app.component.ts
 create mode 100644 alfa-client/apps/info/src/app/app.config.ts
 create mode 100644 alfa-client/apps/info/src/app/app.routes.ts
 create mode 100644 alfa-client/apps/info/src/assets/.gitkeep
 create mode 100644 alfa-client/apps/info/src/favicon.ico
 create mode 100644 alfa-client/apps/info/src/index.html
 create mode 100644 alfa-client/apps/info/src/main.ts
 create mode 100644 alfa-client/apps/info/src/styles.scss
 create mode 100644 alfa-client/apps/info/src/test-setup.ts
 create mode 100644 alfa-client/apps/info/tailwind.config.js
 create mode 100644 alfa-client/apps/info/tsconfig.app.json
 create mode 100644 alfa-client/apps/info/tsconfig.editor.json
 create mode 100644 alfa-client/apps/info/tsconfig.json
 create mode 100644 alfa-client/apps/info/tsconfig.spec.json

diff --git a/alfa-client/apps/info-e2e/.eslintrc.json b/alfa-client/apps/info-e2e/.eslintrc.json
new file mode 100644
index 0000000000..696cb8b121
--- /dev/null
+++ b/alfa-client/apps/info-e2e/.eslintrc.json
@@ -0,0 +1,10 @@
+{
+  "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
+  "ignorePatterns": ["!**/*"],
+  "overrides": [
+    {
+      "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+      "rules": {}
+    }
+  ]
+}
diff --git a/alfa-client/apps/info-e2e/cypress.config.ts b/alfa-client/apps/info-e2e/cypress.config.ts
new file mode 100644
index 0000000000..7df58bdcfa
--- /dev/null
+++ b/alfa-client/apps/info-e2e/cypress.config.ts
@@ -0,0 +1,7 @@
+import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
+
+import { defineConfig } from 'cypress';
+
+export default defineConfig({
+  e2e: { ...nxE2EPreset(__filename, { cypressDir: 'src' }), baseUrl: 'http://localhost:4200' },
+});
diff --git a/alfa-client/apps/info-e2e/project.json b/alfa-client/apps/info-e2e/project.json
new file mode 100644
index 0000000000..2582290b0a
--- /dev/null
+++ b/alfa-client/apps/info-e2e/project.json
@@ -0,0 +1,29 @@
+{
+  "name": "info-e2e",
+  "$schema": "../../node_modules/nx/schemas/project-schema.json",
+  "projectType": "application",
+  "sourceRoot": "apps/info-e2e/src",
+  "tags": [],
+  "implicitDependencies": ["info"],
+  "targets": {
+    "e2e": {
+      "executor": "@nx/cypress:cypress",
+      "options": {
+        "cypressConfig": "apps/info-e2e/cypress.config.ts",
+        "testingType": "e2e",
+        "devServerTarget": "info:serve:development"
+      },
+      "configurations": {
+        "production": {
+          "devServerTarget": "info:serve:production"
+        },
+        "ci": {
+          "devServerTarget": "info:serve-static"
+        }
+      }
+    },
+    "lint": {
+      "executor": "@nx/eslint:lint"
+    }
+  }
+}
diff --git a/alfa-client/apps/info-e2e/src/e2e/app.cy.ts b/alfa-client/apps/info-e2e/src/e2e/app.cy.ts
new file mode 100644
index 0000000000..904740f4a9
--- /dev/null
+++ b/alfa-client/apps/info-e2e/src/e2e/app.cy.ts
@@ -0,0 +1,13 @@
+import { getGreeting } from '../support/app.po';
+
+describe('info-e2e', () => {
+  beforeEach(() => cy.visit('/'));
+
+  it('should display welcome message', () => {
+    // Custom command example, see `../support/commands.ts` file
+    cy.login('my-email@something.com', 'myPassword');
+
+    // Function helper example, see `../support/app.po.ts` file
+    getGreeting().contains(/Welcome/);
+  });
+});
diff --git a/alfa-client/apps/info-e2e/src/fixtures/example.json b/alfa-client/apps/info-e2e/src/fixtures/example.json
new file mode 100644
index 0000000000..02e4254378
--- /dev/null
+++ b/alfa-client/apps/info-e2e/src/fixtures/example.json
@@ -0,0 +1,5 @@
+{
+  "name": "Using fixtures to represent data",
+  "email": "hello@cypress.io",
+  "body": "Fixtures are a great way to mock data for responses to routes"
+}
diff --git a/alfa-client/apps/info-e2e/src/support/app.po.ts b/alfa-client/apps/info-e2e/src/support/app.po.ts
new file mode 100644
index 0000000000..3293424696
--- /dev/null
+++ b/alfa-client/apps/info-e2e/src/support/app.po.ts
@@ -0,0 +1 @@
+export const getGreeting = () => cy.get('h1');
diff --git a/alfa-client/apps/info-e2e/src/support/commands.ts b/alfa-client/apps/info-e2e/src/support/commands.ts
new file mode 100644
index 0000000000..c421a3c47c
--- /dev/null
+++ b/alfa-client/apps/info-e2e/src/support/commands.ts
@@ -0,0 +1,35 @@
+/// <reference types="cypress" />
+
+// ***********************************************
+// This example commands.ts shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+
+// eslint-disable-next-line @typescript-eslint/no-namespace
+declare namespace Cypress {
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  interface Chainable<Subject> {
+    login(email: string, password: string): void;
+  }
+}
+
+// -- This is a parent command --
+Cypress.Commands.add('login', (email, password) => {
+  console.log('Custom command example: Login', email, password);
+});
+//
+// -- This is a child command --
+// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
diff --git a/alfa-client/apps/info-e2e/src/support/e2e.ts b/alfa-client/apps/info-e2e/src/support/e2e.ts
new file mode 100644
index 0000000000..1c1a9e772b
--- /dev/null
+++ b/alfa-client/apps/info-e2e/src/support/e2e.ts
@@ -0,0 +1,17 @@
+// ***********************************************************
+// This example support/e2e.ts is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.ts using ES2015 syntax:
+import './commands';
diff --git a/alfa-client/apps/info-e2e/tsconfig.json b/alfa-client/apps/info-e2e/tsconfig.json
new file mode 100644
index 0000000000..e28de1d791
--- /dev/null
+++ b/alfa-client/apps/info-e2e/tsconfig.json
@@ -0,0 +1,17 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "allowJs": true,
+    "outDir": "../../dist/out-tsc",
+    "module": "commonjs",
+    "types": ["cypress", "node"],
+    "sourceMap": false,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "noImplicitOverride": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "include": ["**/*.ts", "**/*.js", "cypress.config.ts", "**/*.cy.ts", "**/*.cy.js", "**/*.d.ts"]
+}
diff --git a/alfa-client/apps/info/.eslintrc.json b/alfa-client/apps/info/.eslintrc.json
new file mode 100644
index 0000000000..437641b9a1
--- /dev/null
+++ b/alfa-client/apps/info/.eslintrc.json
@@ -0,0 +1,33 @@
+{
+  "extends": ["../../.eslintrc.json"],
+  "ignorePatterns": ["!**/*"],
+  "overrides": [
+    {
+      "files": ["*.ts"],
+      "extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
+      "rules": {
+        "@angular-eslint/directive-selector": [
+          "error",
+          {
+            "type": "attribute",
+            "prefix": "app",
+            "style": "camelCase"
+          }
+        ],
+        "@angular-eslint/component-selector": [
+          "error",
+          {
+            "type": "element",
+            "prefix": "app",
+            "style": "kebab-case"
+          }
+        ]
+      }
+    },
+    {
+      "files": ["*.html"],
+      "extends": ["plugin:@nx/angular-template"],
+      "rules": {}
+    }
+  ]
+}
diff --git a/alfa-client/apps/info/jest.config.ts b/alfa-client/apps/info/jest.config.ts
new file mode 100644
index 0000000000..ae2366cf2a
--- /dev/null
+++ b/alfa-client/apps/info/jest.config.ts
@@ -0,0 +1,22 @@
+/* eslint-disable */
+export default {
+  displayName: 'info',
+  preset: '../../jest.preset.js',
+  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
+  coverageDirectory: '../../coverage/apps/info',
+  transform: {
+    '^.+\\.(ts|mjs|js|html)$': [
+      'jest-preset-angular',
+      {
+        tsconfig: '<rootDir>/tsconfig.spec.json',
+        stringifyContentPathRegex: '\\.(html|svg)$',
+      },
+    ],
+  },
+  transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
+  snapshotSerializers: [
+    'jest-preset-angular/build/serializers/no-ng-attributes',
+    'jest-preset-angular/build/serializers/ng-snapshot',
+    'jest-preset-angular/build/serializers/html-comment',
+  ],
+};
diff --git a/alfa-client/apps/info/project.json b/alfa-client/apps/info/project.json
new file mode 100644
index 0000000000..2a2d853596
--- /dev/null
+++ b/alfa-client/apps/info/project.json
@@ -0,0 +1,95 @@
+{
+  "name": "info",
+  "$schema": "../../node_modules/nx/schemas/project-schema.json",
+  "projectType": "application",
+  "prefix": "app",
+  "sourceRoot": "apps/info/src",
+  "tags": [],
+  "targets": {
+    "build": {
+      "executor": "@angular-devkit/build-angular:browser",
+      "outputs": ["{options.outputPath}"],
+      "options": {
+        "outputPath": "dist/apps/info",
+        "index": "apps/info/src/index.html",
+        "main": "apps/info/src/main.ts",
+        "polyfills": ["zone.js"],
+        "tsConfig": "apps/info/tsconfig.app.json",
+        "inlineStyleLanguage": "scss",
+        "assets": ["apps/info/src/favicon.ico", "apps/info/src/assets"],
+        "styles": ["apps/info/src/styles.scss"],
+        "scripts": [],
+        "stylePreprocessorOptions": {
+          "includePaths": [
+            "apps/alfa/src/styles/abstracts",
+            "node_modules/@angular",
+            "node_modules/include-media",
+            "node_modules/typeface-roboto"
+          ]
+        }
+      },
+      "configurations": {
+        "production": {
+          "budgets": [
+            {
+              "type": "initial",
+              "maximumWarning": "500kb",
+              "maximumError": "1mb"
+            },
+            {
+              "type": "anyComponentStyle",
+              "maximumWarning": "2kb",
+              "maximumError": "4kb"
+            }
+          ],
+          "outputHashing": "all"
+        },
+        "development": {
+          "buildOptimizer": false,
+          "optimization": false,
+          "vendorChunk": true,
+          "extractLicenses": false,
+          "sourceMap": true,
+          "namedChunks": true
+        }
+      },
+      "defaultConfiguration": "production"
+    },
+    "serve": {
+      "executor": "@angular-devkit/build-angular:dev-server",
+      "configurations": {
+        "production": {
+          "buildTarget": "info:build:production"
+        },
+        "development": {
+          "buildTarget": "info:build:development"
+        }
+      },
+      "defaultConfiguration": "development"
+    },
+    "extract-i18n": {
+      "executor": "@angular-devkit/build-angular:extract-i18n",
+      "options": {
+        "buildTarget": "info:build"
+      }
+    },
+    "lint": {
+      "executor": "@nx/eslint:lint"
+    },
+    "test": {
+      "executor": "@nx/jest:jest",
+      "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
+      "options": {
+        "jestConfig": "apps/info/jest.config.ts"
+      }
+    },
+    "serve-static": {
+      "executor": "@nx/web:file-server",
+      "options": {
+        "buildTarget": "info:build",
+        "port": 4200,
+        "spa": true
+      }
+    }
+  }
+}
diff --git a/alfa-client/apps/info/src/app/app.component.html b/alfa-client/apps/info/src/app/app.component.html
new file mode 100644
index 0000000000..8a91938079
--- /dev/null
+++ b/alfa-client/apps/info/src/app/app.component.html
@@ -0,0 +1,115 @@
+<header
+  class="flex h-16 items-center justify-between border-b border-b-ozggray-300 bg-white px-9 py-2"
+  data-test-id="admin-header"
+>
+  <a
+    class="rounded border-2 border-transparent p-1 outline-2 outline-offset-2 hover:border-primary focus-visible:border-gray-200 focus-visible:outline-focus"
+    aria-label="OZG-Cloud Administration"
+    routerLink="/"
+    data-test-id="logo-link"
+  >
+    LOGO
+  </a>
+</header>
+<div class="relative flex w-full flex-auto justify-center">
+  <main class="flex-auto bg-background-50 p-6">
+    <div class="ozg-prose prose">
+      <h1>Erklärung zur Barrierefreiheit</h1>
+      <p>
+        Die Staatskanzlei des Ministerpräsidenten ist bemüht, ihre Anwendung im Einklang mit § 11 Absatz 1
+        Landesbehindertengleichstellungsgesetz (LBGG) sowie den Anforderungen der Barrierefreiheit gemäß § 13 Absatz 3 LBGG
+        barrierefrei zugänglich zu machen.
+      </p>
+      <p>Diese Erklärung zur Barrierefreiheit gilt für:</p>
+      <ul>
+        <li>
+          <a href="https://se.kop.schleswig-holstein.de" target="_blank">https://se.kop.schleswig-holstein.de</a>
+        </li>
+        <li>
+          <a href="https://luebeck.kop.schleswig-holstein.de" target="_blank">https://luebeck.kop.schleswig-holstein.de</a>
+        </li>
+        <li>
+          <a href="https://kiel.kop.schleswig-holstein.de" target="_blank">https://kiel.kop.schleswig-holstein.de</a>
+        </li>
+      </ul>
+      <h2>Stand der Vereinbarkeit mit den Anforderungen</h2>
+      <p>Diese Website/mobile Anwendung ist teilweise mit § 13 Absatz 3 LBGG vereinbar.</p>
+      <h2>Nicht barrierefreie Inhalte</h2>
+      <p>Die nachstehend aufgeführten Inhalte sind unvereinbar mit § 13 Absatz 3 LBGG und somit nicht barrierefrei:</p>
+      <ul>
+        <li>Erläuterungen zu den Inhalten und der Navigation dieser Anwendung in leichter Sprache sind nicht vorhanden.</li>
+        <li>
+          Responsive Darstellung ist nicht vorhanden, weshalb Inhalte nur bedingt verlustfrei vergrößerbar sind oder umbrechen.
+        </li>
+        <li>Gliedernde Überschriften und Bereiche sind nicht umfassend vorhanden.</li>
+        <li>Benutzerdefinierte Einstellungen (Farbauswahl und Schriftgröße) werden nicht umfänglich angewendet.</li>
+        <li>Die Einstellungsmöglichkeiten im oberen Navigationsmenü können nicht per Tastatursteuerung erreicht werden.</li>
+        <li>Die Vorschläge der Vorgangssuche sind nicht per Screenreader wahrnehmbar.</li>
+        <li>Vereinzelt treten Texte mit zu geringem Kontrast zum Hintergrund auf.</li>
+        <li>Vereinzelt ist der Tastaturfokus nur schwer wahrnehmbar. Bei Datumspickern ist keine Fokushervorhebung vorhanden.</li>
+        <li>
+          Es fehlen Statusmeldungen, die Screenreader-Nutzende über Änderungen der Suchergebnisse oder über Änderungen von
+          Anträgen informieren.
+        </li>
+        <li>
+          Der als PDF zur Verfügung gestellte Benutzerleitfaden, sowie die als PDF abgespeicherten Nachrichten der Anwendung
+          entsprechen nicht dem PDF/UA-Standard.
+        </li>
+      </ul>
+      <p>
+        Die Anwendung befindet sich derzeit noch in der Entwicklung. An bestehenden und bekannten Barrieren wird gearbeitet, um
+        eine Barrierefreiheit gemäß § 13 Absatz 3 LBGG gewährleisten zu können.
+      </p>
+      <h2>Erstellung dieser Erklärung zur Barrierefreiheit</h2>
+      <p>Diese Erklärung wurde am 15.07.2024 erstellt.</p>
+      <p>
+        Die Aussagen bezüglich der Vereinbarkeit mit den Barrierefreiheitsanforderungen in dieser Erklärung beruhen auf einem
+        Prüfbericht von Dataport vom 08.04.2024 und einer Selbstbewertung.
+      </p>
+      <p>Die Erklärung wurde zuletzt am 15.07.2024 aktualisiert.</p>
+      <h2>Feedback und Kontaktangaben</h2>
+      <p>
+        Sie möchten uns bestehende Barrieren mitteilen oder Informationen zur Umsetzung der Barrierefreiheit erfragen? Für Ihr
+        Feedback sowie alle weiteren Informationen sprechen Sie unsere Verantwortlichen Kontaktpersonen unter
+        <a href="mailto:digitalisierung@stk.landsh.de">digitalisierung&commat;stk.landsh.de</a> an.
+      </p>
+      <h2>Beschwerdeverfahren</h2>
+      <p>
+        Wenn auch nach Ihrem Feedback an den oben genannten Kontakt keine zufriedenstellende Lösung gefunden wurde, können Sie
+        sich an die Beschwerdestelle des Landes Schleswig-Holstein gemäß Landesbehindertengleichstellungsgesetz (LBGG) wenden. Die
+        Beschwerdestelle hat die Aufgabe, Konflikte zum Thema Barrierefreiheit zwischen Menschen mit Behinderungen und
+        öffentlichen Stellen in Schleswig-Holstein zu lösen. Dabei geht es nicht darum, Gewinner oder Verlierer zu finden.
+        Vielmehr ist es das Ziel, mit Hilfe der Beschwerdestelle gemeinsam und außergerichtlich eine Lösung für ein Problem zu
+        finden. Das Beschwerdeverfahren ist kostenlos. Es muss kein Rechtsbeistand eingeschaltet werden.
+      </p>
+      <p>
+        Auf der
+        <a href="https://www.landtag.ltsh.de/beauftragte/beschwerdestelle-fuer-barrieren/" target="_blank"
+          >Internetseite der Beschwerdestelle</a
+        >
+        finden Sie alle Informationen zum Beschwerdeverfahren. Dort können Sie nachlesen, wie ein Beschwerdeverfahren abläuft.
+      </p>
+      <p>Sie erreichen die Beschwerdestelle unter folgender Adresse:</p>
+      <p>Beschwerdestelle nach dem Behindertengleichstellungsgesetz bei der Landesbeauftragten für Menschen mit Behinderung</p>
+      <p>Büroanschrift: Karolinenweg 1 24105 Kiel</p>
+      <p>Postanschrift: Postfach 7121 24171 Kiel</p>
+      <p>Telefon: +49 431 988 1620</p>
+      <p>
+        E-Mail:
+        <a href="mailto:bbit@landtag.ltsh.de">bbit&commat;landtag.ltsh.de</a>
+      </p>
+    </div>
+
+    <footer class="ozg-prose">
+      <nav>
+        <ul class="flex flex-row flex-wrap justify-center gap-9 pt-14">
+          <li><a routerLink="/barrierefreiheit">Barrierefreiheit</a></li>
+          <li><a routerLink="/datenschutzerklarrung">Datenschutzerklärung</a></li>
+          <li><a routerLink="/impressum">Impressum</a></li>
+        </ul>
+      </nav>
+    </footer>
+
+    <router-outlet></router-outlet>
+  </main>
+</div>
diff --git a/alfa-client/apps/info/src/app/app.component.scss b/alfa-client/apps/info/src/app/app.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/alfa-client/apps/info/src/app/app.component.spec.ts b/alfa-client/apps/info/src/app/app.component.spec.ts
new file mode 100644
index 0000000000..0f49c5d7e8
--- /dev/null
+++ b/alfa-client/apps/info/src/app/app.component.spec.ts
@@ -0,0 +1,25 @@
+import { TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { AppComponent } from './app.component';
+import { NxWelcomeComponent } from './nx-welcome.component';
+
+describe('AppComponent', () => {
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [AppComponent, NxWelcomeComponent, RouterTestingModule],
+    }).compileComponents();
+  });
+
+  it('should render title', () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    fixture.detectChanges();
+    const compiled = fixture.nativeElement as HTMLElement;
+    expect(compiled.querySelector('h1')?.textContent).toContain('Welcome info');
+  });
+
+  it(`should have as title 'info'`, () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.componentInstance;
+    expect(app.title).toEqual('info');
+  });
+});
diff --git a/alfa-client/apps/info/src/app/app.component.ts b/alfa-client/apps/info/src/app/app.component.ts
new file mode 100644
index 0000000000..0b1ed01b56
--- /dev/null
+++ b/alfa-client/apps/info/src/app/app.component.ts
@@ -0,0 +1,13 @@
+import { Component } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+@Component({
+  standalone: true,
+  imports: [RouterModule],
+  selector: 'app-root',
+  templateUrl: './app.component.html',
+  styleUrl: './app.component.scss',
+})
+export class AppComponent {
+  title = 'info';
+}
diff --git a/alfa-client/apps/info/src/app/app.config.ts b/alfa-client/apps/info/src/app/app.config.ts
new file mode 100644
index 0000000000..ed404941f7
--- /dev/null
+++ b/alfa-client/apps/info/src/app/app.config.ts
@@ -0,0 +1,7 @@
+import { ApplicationConfig } from '@angular/core';
+import { provideRouter } from '@angular/router';
+import { appRoutes } from './app.routes';
+
+export const appConfig: ApplicationConfig = {
+  providers: [provideRouter(appRoutes)],
+};
diff --git a/alfa-client/apps/info/src/app/app.routes.ts b/alfa-client/apps/info/src/app/app.routes.ts
new file mode 100644
index 0000000000..8762dfe2c6
--- /dev/null
+++ b/alfa-client/apps/info/src/app/app.routes.ts
@@ -0,0 +1,3 @@
+import { Route } from '@angular/router';
+
+export const appRoutes: Route[] = [];
diff --git a/alfa-client/apps/info/src/assets/.gitkeep b/alfa-client/apps/info/src/assets/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/alfa-client/apps/info/src/favicon.ico b/alfa-client/apps/info/src/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..317ebcb2336e0833a22dddf0ab287849f26fda57
GIT binary patch
literal 15086
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
V$iTpo%)r1?&%hv44;phuU;z8c-N*m{

literal 0
HcmV?d00001

diff --git a/alfa-client/apps/info/src/index.html b/alfa-client/apps/info/src/index.html
new file mode 100644
index 0000000000..1c98446a11
--- /dev/null
+++ b/alfa-client/apps/info/src/index.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en" class="h-full bg-white antialiased">
+  <head>
+    <meta charset="utf-8" />
+    <title>Info</title>
+    <base href="/" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <link rel="icon" type="image/x-icon" href="favicon.ico" />
+  </head>
+  <body class="flex min-h-full bg-white text-black">
+    <app-root class="flex w-full flex-col"></app-root>
+  </body>
+</html>
diff --git a/alfa-client/apps/info/src/main.ts b/alfa-client/apps/info/src/main.ts
new file mode 100644
index 0000000000..7205a13df9
--- /dev/null
+++ b/alfa-client/apps/info/src/main.ts
@@ -0,0 +1,5 @@
+import { bootstrapApplication } from '@angular/platform-browser';
+import { AppComponent } from './app/app.component';
+import { appConfig } from './app/app.config';
+
+bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
diff --git a/alfa-client/apps/info/src/styles.scss b/alfa-client/apps/info/src/styles.scss
new file mode 100644
index 0000000000..e8b9bb8f36
--- /dev/null
+++ b/alfa-client/apps/info/src/styles.scss
@@ -0,0 +1,11 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@import 'libs/design-system/src/lib/tailwind-preset/root.css';
+
+@layer components {
+  .ozg-prose a {
+    @apply text-primary no-underline hover:text-primary-hover hover:underline focus-visible:rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus;
+  }
+}
diff --git a/alfa-client/apps/info/src/test-setup.ts b/alfa-client/apps/info/src/test-setup.ts
new file mode 100644
index 0000000000..ab1eeeb335
--- /dev/null
+++ b/alfa-client/apps/info/src/test-setup.ts
@@ -0,0 +1,8 @@
+// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment
+globalThis.ngJest = {
+  testEnvironmentOptions: {
+    errorOnUnknownElements: true,
+    errorOnUnknownProperties: true,
+  },
+};
+import 'jest-preset-angular/setup-jest';
diff --git a/alfa-client/apps/info/tailwind.config.js b/alfa-client/apps/info/tailwind.config.js
new file mode 100644
index 0000000000..9fd1192933
--- /dev/null
+++ b/alfa-client/apps/info/tailwind.config.js
@@ -0,0 +1,16 @@
+/* eslint-env node */
+/* eslint @typescript-eslint/no-var-requires: "off" */
+
+const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind');
+const { join } = require('path');
+const sharedTailwindConfig = require('../../libs/design-system/src/lib/tailwind-preset/tailwind.config.js');
+
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+  presets: [sharedTailwindConfig],
+  content: [join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), ...createGlobPatternsForDependencies(__dirname)],
+  theme: {
+    extend: {},
+  },
+  plugins: [],
+};
diff --git a/alfa-client/apps/info/tsconfig.app.json b/alfa-client/apps/info/tsconfig.app.json
new file mode 100644
index 0000000000..fff4a41d44
--- /dev/null
+++ b/alfa-client/apps/info/tsconfig.app.json
@@ -0,0 +1,10 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../dist/out-tsc",
+    "types": []
+  },
+  "files": ["src/main.ts"],
+  "include": ["src/**/*.d.ts"],
+  "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
+}
diff --git a/alfa-client/apps/info/tsconfig.editor.json b/alfa-client/apps/info/tsconfig.editor.json
new file mode 100644
index 0000000000..a8ac182c08
--- /dev/null
+++ b/alfa-client/apps/info/tsconfig.editor.json
@@ -0,0 +1,6 @@
+{
+  "extends": "./tsconfig.json",
+  "include": ["src/**/*.ts"],
+  "compilerOptions": {},
+  "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
+}
diff --git a/alfa-client/apps/info/tsconfig.json b/alfa-client/apps/info/tsconfig.json
new file mode 100644
index 0000000000..de18e88adc
--- /dev/null
+++ b/alfa-client/apps/info/tsconfig.json
@@ -0,0 +1,32 @@
+{
+  "compilerOptions": {
+    "target": "es2022",
+    "useDefineForClassFields": false,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "noImplicitOverride": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "files": [],
+  "include": [],
+  "references": [
+    {
+      "path": "./tsconfig.editor.json"
+    },
+    {
+      "path": "./tsconfig.app.json"
+    },
+    {
+      "path": "./tsconfig.spec.json"
+    }
+  ],
+  "extends": "../../tsconfig.base.json",
+  "angularCompilerOptions": {
+    "enableI18nLegacyMessageIdFormat": false,
+    "strictInjectionParameters": true,
+    "strictInputAccessModifiers": true,
+    "strictTemplates": true
+  }
+}
diff --git a/alfa-client/apps/info/tsconfig.spec.json b/alfa-client/apps/info/tsconfig.spec.json
new file mode 100644
index 0000000000..7870b7c011
--- /dev/null
+++ b/alfa-client/apps/info/tsconfig.spec.json
@@ -0,0 +1,11 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../dist/out-tsc",
+    "module": "commonjs",
+    "target": "es2016",
+    "types": ["jest", "node"]
+  },
+  "files": ["src/test-setup.ts"],
+  "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
+}
diff --git a/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css b/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css
index 22790260b6..00d1a2d067 100644
--- a/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css
+++ b/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css
@@ -3,6 +3,13 @@
 @tailwind utilities;
 
 @import '~@angular/cdk/overlay-prebuilt.css';
+@import 'typeface-roboto/index.css';
+
+@layer base {
+  html {
+    font-family: 'Roboto', 'Helvetica Neue', sans-serif;
+  }
+}
 
 :root {
   --warning: 38 92% 50%;
diff --git a/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js b/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js
index 88f03501ec..68734c4d0c 100644
--- a/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js
+++ b/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js
@@ -130,5 +130,5 @@ module.exports = {
       },
     },
   },
-  plugins: [],
+  plugins: [require('@tailwindcss/typography')],
 };
diff --git a/alfa-client/nx.json b/alfa-client/nx.json
index d5846d9ce5..d4a997871f 100644
--- a/alfa-client/nx.json
+++ b/alfa-client/nx.json
@@ -4,10 +4,10 @@
   },
   "generators": {
     "@nx/angular:application": {
-      "style": "scss",
+      "e2eTestRunner": "cypress",
       "linter": "eslint",
-      "unitTestRunner": "jest",
-      "e2eTestRunner": "cypress"
+      "style": "scss",
+      "unitTestRunner": "jest"
     },
     "@nx/angular:library": {
       "linter": "eslint",
@@ -67,6 +67,11 @@
     "@nx/eslint:lint": {
       "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
       "cache": true
+    },
+    "@angular-devkit/build-angular:browser": {
+      "cache": true,
+      "dependsOn": ["^build"],
+      "inputs": ["production", "^production"]
     }
   },
   "namedInputs": {
diff --git a/alfa-client/package-lock.json b/alfa-client/package-lock.json
index f5d4ef0b6b..60754866c3 100644
--- a/alfa-client/package-lock.json
+++ b/alfa-client/package-lock.json
@@ -79,6 +79,7 @@
         "@swc-node/register": "1.9.1",
         "@swc/core": "~1.5.7",
         "@swc/helpers": "~0.5.2",
+        "@tailwindcss/typography": "^0.5.15",
         "@testing-library/jest-dom": "6.4.5",
         "@types/file-saver": "2.0.7",
         "@types/jest": "29.4.4",
@@ -15388,6 +15389,36 @@
         "@swc/counter": "^0.1.3"
       }
     },
+    "node_modules/@tailwindcss/typography": {
+      "version": "0.5.15",
+      "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/@tailwindcss/typography/-/typography-0.5.15.tgz",
+      "integrity": "sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "lodash.castarray": "^4.4.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.merge": "^4.6.2",
+        "postcss-selector-parser": "6.0.10"
+      },
+      "peerDependencies": {
+        "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20"
+      }
+    },
+    "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
+      "version": "6.0.10",
+      "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+      "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/@testing-library/jest-dom": {
       "version": "6.4.5",
       "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/@testing-library/jest-dom/-/jest-dom-6.4.5.tgz",
@@ -28163,6 +28194,13 @@
       "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
       "license": "MIT"
     },
+    "node_modules/lodash.castarray": {
+      "version": "4.4.0",
+      "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
+      "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/lodash.debounce": {
       "version": "4.0.8",
       "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -28190,6 +28228,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/lodash.isstring": {
       "version": "4.0.1",
       "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
diff --git a/alfa-client/package.json b/alfa-client/package.json
index b081dded54..c6d61cc487 100644
--- a/alfa-client/package.json
+++ b/alfa-client/package.json
@@ -7,6 +7,7 @@
     "start:admin": "nx run admin:serve --port 4301 --disable-host-check",
     "start:demo": "nx run demo:serve --port 4500 --disable-host-check",
     "start:debug": "nx run alfa:serve --port 4300 --disable-host-check --verbose",
+    "start:info": "nx run info:serve --port 4600 --disable-host-check",
     "start-for-screenreader": "nx run alfa:serve --host 192.168.178.20 --port 4300 --disable-host-check --verbose",
     "start:devbe": "nx run alfa:serve --port 4300 --disable-host-check --proxy-config proxy.dev.conf.json --verbose",
     "build": "nx run alfa:build",
@@ -121,6 +122,7 @@
     "@swc-node/register": "1.9.1",
     "@swc/core": "~1.5.7",
     "@swc/helpers": "~0.5.2",
+    "@tailwindcss/typography": "^0.5.15",
     "@testing-library/jest-dom": "6.4.5",
     "@types/file-saver": "2.0.7",
     "@types/jest": "29.4.4",
-- 
GitLab