Repository: inovex/scrumlr.io
Pull request #3804 (2024-01-30 16:41:38)

style: implement new design of passphrase page
## Description

Implement the new design of the passphrase page. 

## Changelog
<!-- Please provide a detailed list of the changes you have made to the codebase. -->

## Checklist

- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] The light- and dark-theme are both supported and tested
- [ ] The design was implemented and is responsive for all devices and screen sizes
- [ ] The application was tested in the most commonly used browsers (e.g. Chrome, Firefox, Safari)

## (Optional) Visual Changes
<!-- If available, please provide a before and after screenshot of your UI related changes. -->



Patch:
diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json
index 7f0c2643..4643671a 100644
--- a/public/locales/de/translation.json
+++ b/public/locales/de/translation.json
@@ -157,9 +157,11 @@
     "create": "Kärtchen erstellen"
   },
   "PassphraseDialog": {
-    "passphraseInputLabel": "Gib das Passwort ein, um beizutreten",
-    "togglePassphraseVisibility": "Schalte Sichtbarkeit des Passworts um",
-    "submit": "Absenden"
+    "showPassword": "Zeige das Passwort",
+    "hidePassword": "Verberge das Passwort",
+    "label": "Gib das Passwort ein, um dieses Board zu öffnen",
+    "submit": "Board öffnen",
+    "inputPlaceholder": "Passwort"
   },
   "Timer": {
     "endTimer": "Timer beenden",
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index d4320239..1493b524 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -157,9 +157,11 @@
     "create": "Create note"
   },
   "PassphraseDialog": {
-    "passphraseInputLabel": "Enter the passphrase to access this board",
-    "togglePassphraseVisibility": "Toggle passphrase visibility",
-    "submit": "Submit"
+    "showPassword": "Show password",
+    "hidePassword": "Hide password",
+    "label": "Enter the password to open this board",
+    "submit": "Open board",
+    "inputPlaceholder": "Password"
   },
   "Timer": {
     "endTimer": "End timer",
diff --git a/src/assets/backgrounds/bg--dark.svg b/src/assets/backgrounds/bg--dark.svg
new file mode 100644
index 00000000..1c22c544
--- /dev/null
+++ b/src/assets/backgrounds/bg--dark.svg
@@ -0,0 +1,24 @@
+<svg width="1920" height="1080" viewBox="0 0 1920 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2991 0H-843V1080H2991V0Z" fill="#242C3D"/>
+<path d="M2995 0H-843V1080H2995V0Z" fill="#242C3D"/>
+<path opacity="0.1" d="M-845 0H2987V1080H-845V0Z" fill="url(#paint0_linear_5540_13800)"/>
+<path d="M-700.831 -55.5009C-700.831 -55.5009 -663.549 -26.5506 -603.231 16.3864C-588.608 77.936 -619.792 143.445 -545.925 195.556C-348.557 334.807 30.5415 581.14 406.721 256.308C521.079 157.559 427.023 129.524 693.749 -27.2868C960.475 -184.098 1265.92 -342.607 1473.63 -370.936C1406.63 -566.216 388.034 -560.733 126.778 -556.21C-387.425 -547.308 -319.807 -198.313 -319.807 -198.313L-708.392 -319.665L-700.831 -55.5009Z" fill="url(#paint1_linear_5540_13800)"/>
+<path d="M1475 167.999C1479.42 167.999 1483 164.417 1483 159.999C1483 155.581 1479.42 151.999 1475 151.999C1470.58 151.999 1467 155.581 1467 159.999C1467 164.417 1470.58 167.999 1475 167.999Z" fill="#5A647D"/>
+<path d="M151 905.999C157.075 905.999 162 901.074 162 894.999C162 888.924 157.075 883.999 151 883.999C144.925 883.999 140 888.924 140 894.999C140 901.074 144.925 905.999 151 905.999Z" fill="#5A647D"/>
+<path d="M213 212.999C217.418 212.999 221 209.417 221 204.999C221 200.581 217.418 196.999 213 196.999C208.582 196.999 205 200.581 205 204.999C205 209.417 208.582 212.999 213 212.999Z" fill="#5A647D"/>
+<path d="M1734 932.999C1742.28 932.999 1749 926.283 1749 917.999C1749 909.715 1742.28 902.999 1734 902.999C1725.72 902.999 1719 909.715 1719 917.999C1719 926.283 1725.72 932.999 1734 932.999Z" fill="#5A647D"/>
+<path d="M228 976.999C231.866 976.999 235 973.865 235 969.999C235 966.133 231.866 962.999 228 962.999C224.134 962.999 221 966.133 221 969.999C221 973.865 224.134 976.999 228 976.999Z" fill="#5A647D"/>
+<path d="M1516.5 145C1523.4 145 1529 139.404 1529 132.5C1529 125.596 1523.4 120 1516.5 120C1509.6 120 1504 125.596 1504 132.5C1504 139.404 1509.6 145 1516.5 145Z" fill="#5A647D"/>
+<defs>
+<linearGradient id="paint0_linear_5540_13800" x1="2824.5" y1="979.5" x2="2239.9" y2="-652.775" gradientUnits="userSpaceOnUse">
+<stop stop-color="#181D27"/>
+<stop offset="0.321108" stop-color="#171F30"/>
+<stop offset="0.737497" stop-color="#4A5775"/>
+<stop offset="1" stop-color="#7385B2"/>
+</linearGradient>
+<linearGradient id="paint1_linear_5540_13800" x1="-357.501" y1="384.5" x2="664.499" y2="116" gradientUnits="userSpaceOnUse">
+<stop stop-color="#2C3549"/>
+<stop offset="1" stop-color="#35435E" stop-opacity="0.03"/>
+</linearGradient>
+</defs>
+</svg>
diff --git a/src/assets/backgrounds/bg--light.svg b/src/assets/backgrounds/bg--light.svg
new file mode 100644
index 00000000..139e07d4
--- /dev/null
+++ b/src/assets/backgrounds/bg--light.svg
@@ -0,0 +1,29 @@
+<svg width="1920" height="1080" viewBox="0 0 1920 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2995 0H-845V1080H2995V0Z" fill="white"/>
+<path d="M2995 -80H-845V1080H2995V-80Z" fill="#F5F8FF"/>
+<path opacity="0.2" d="M2995 -101H-845V1080H2995V-101Z" fill="url(#paint0_linear_5540_13801)"/>
+<path opacity="0.03" d="M-845 -1H2995V1080H-845V-1Z" fill="url(#paint1_linear_5540_13801)"/>
+<path d="M-690.831 -55.5009C-690.831 -55.5009 -653.549 -26.5506 -593.231 16.3864C-578.608 77.936 -609.792 143.445 -535.925 195.556C-338.557 334.807 40.5415 581.14 416.721 256.308C531.079 157.559 437.023 129.524 703.749 -27.2868C970.475 -184.098 1275.92 -342.607 1483.63 -370.936C1416.63 -566.216 398.034 -560.733 136.778 -556.21C-377.425 -547.308 -309.807 -198.313 -309.807 -198.313L-698.392 -319.665L-690.831 -55.5009Z" fill="url(#paint2_linear_5540_13801)"/>
+<path d="M1485 167.999C1489.42 167.999 1493 164.417 1493 159.999C1493 155.581 1489.42 151.999 1485 151.999C1480.58 151.999 1477 155.581 1477 159.999C1477 164.417 1480.58 167.999 1485 167.999Z" fill="white"/>
+<path d="M161 905.999C167.075 905.999 172 901.074 172 894.999C172 888.924 167.075 883.999 161 883.999C154.925 883.999 150 888.924 150 894.999C150 901.074 154.925 905.999 161 905.999Z" fill="white"/>
+<path d="M223 212.999C227.418 212.999 231 209.417 231 204.999C231 200.581 227.418 196.999 223 196.999C218.582 196.999 215 200.581 215 204.999C215 209.417 218.582 212.999 223 212.999Z" fill="white"/>
+<path d="M1744 932.999C1752.28 932.999 1759 926.283 1759 917.999C1759 909.715 1752.28 902.999 1744 902.999C1735.72 902.999 1729 909.715 1729 917.999C1729 926.283 1735.72 932.999 1744 932.999Z" fill="white"/>
+<path d="M238 976.999C241.866 976.999 245 973.865 245 969.999C245 966.133 241.866 962.999 238 962.999C234.134 962.999 231 966.133 231 969.999C231 973.865 234.134 976.999 238 976.999Z" fill="white"/>
+<path d="M1526.5 145C1533.4 145 1539 139.404 1539 132.5C1539 125.596 1533.4 120 1526.5 120C1519.6 120 1514 125.596 1514 132.5C1514 139.404 1519.6 145 1526.5 145Z" fill="white"/>
+<defs>
+<linearGradient id="paint0_linear_5540_13801" x1="3252.28" y1="938.28" x2="2236" y2="-1536.52" gradientUnits="userSpaceOnUse">
+<stop stop-color="#0057FF" stop-opacity="0.365"/>
+<stop offset="0.373" stop-color="#D1E0FF"/>
+<stop offset="0.666" stop-color="#FFF2F7"/>
+<stop offset="1" stop-color="#FFE0ED"/>
+</linearGradient>
+<linearGradient id="paint1_linear_5540_13801" x1="1075" y1="-1" x2="1075" y2="1080" gradientUnits="userSpaceOnUse">
+<stop stop-color="#6B9CFF"/>
+<stop offset="1" stop-color="white"/>
+</linearGradient>
+<linearGradient id="paint2_linear_5540_13801" x1="224.913" y1="472.29" x2="468.312" y2="-714.325" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FFF2F7" stop-opacity="0.749"/>
+<stop offset="1" stop-color="#EDF1FC"/>
+</linearGradient>
+</defs>
+</svg>
diff --git a/src/assets/icon-arrow-right.svg b/src/assets/icon-arrow-right.svg
index 2324f838..0df90440 100644
--- a/src/assets/icon-arrow-right.svg
+++ b/src/assets/icon-arrow-right.svg
@@ -1,8 +1,4 @@
-<svg viewBox="0 0 34 34" xmlns="http://www.w3.org/2000/svg">
-<g fill="none" stroke-linecap="square">
-<g stroke="currentColor" stroke-width="3">
-<line x1="2.5" x2="25.5" y1="17.5" y2="17.5"/>
-<path d="m19.033 10.025 7.3223 6.7365c0.40644 0.37393 0.4328 1.0065 0.058877 1.413-0.018299 0.019891-0.037397 0.039031-0.057247 0.057374l-7.324 6.7681"/>
-</g>
-</g>
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 32 32">
+  <path stroke="currentColor" stroke-linecap="square" stroke-width="2" d="M19.219 11 24 16m0 0-4.782 5"/>
+  <path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M7 16.078h14.642"/>
 </svg>
diff --git a/src/assets/icon-key.svg b/src/assets/icon-key.svg
index 983cfcfc..ba81eae7 100644
--- a/src/assets/icon-key.svg
+++ b/src/assets/icon-key.svg
@@ -1,1 +1,4 @@
-<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"></path></svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" fill="none" viewBox="0 0 26 26">
+  <path fill="currentColor" fill-rule="evenodd" d="M10 9a7 7 0 1 1 14 0c0 3.799-3.005 6.07-5.664 6.514-1.292.215-2.596-.012-3.389-.408a1 1 0 0 0-1.17.203L12.086 17s-.013.012-.048.014a9.917 9.917 0 0 0-1.273.137 4.497 4.497 0 0 0-.719.189 2.06 2.06 0 0 0-.753.454c-.318.318-.468.757-.55 1.034-.09.309-.16.658-.219.954l-.005.023c-.064.324-.118.59-.181.805-.02.067-.038.116-.051.151l-.022.005a3.774 3.774 0 0 1-.56.054l-.15.006a10.29 10.29 0 0 0-.627.038c-.232.024-.627.078-.965.293-.406.259-.586.647-.672.924a3.576 3.576 0 0 0-.132.774c-.009.116-.014.204-.019.281-.006.1-.01.181-.021.282-.01.091-.02.137-.023.153l-.001.004a4.97 4.97 0 0 0-.16.375l-.017.042-.003.009H2v-3.086l8.19-8.19a1.002 1.002 0 0 0 .195-1.19C10.015 10.782 10 10.018 10 9Zm-1.677 2.763C7.999 10.788 8 9.848 8 9.066V9a9 9 0 1 1 18 0c0 5.038-3.995 7.93-7.336 8.486-1.374.23-2.796.089-3.93-.306L13.5 18.414a2.068 2.068 0 0 1-1.366.597c-.287.014-.67.044-1.006.106a2.533 2.533 0 0 0-.394.1l-.014.005c-.014.035-.034.09-.058.17-.063.214-.117.48-.181.804l-.005.023c-.06.296-.129.645-.22.954-.081.277-.23.716-.549 1.034-.33.33-.746.456-1.035.516a5.638 5.638 0 0 1-.877.094l-.208.009a8.085 8.085 0 0 0-.418.023 3.863 3.863 0 0 0-.028.334c-.009.14-.02.314-.033.443-.022.203-.065.525-.214.821a2.52 2.52 0 0 0-.09.214l-.026.065a2.55 2.55 0 0 1-.25.497c-.14.21-.338.408-.616.552a2.16 2.16 0 0 1-.831.222L5.04 26H1.5A1.5 1.5 0 0 1 0 24.5v-3.586A2 2 0 0 1 .586 19.5l7.737-7.737ZM7.205 22.662l-.001.003.001-.003Z" clip-rule="evenodd"/>
+  <circle cx="19" cy="7" r="2.25" stroke="currentColor" stroke-opacity=".4" stroke-width="1.5"/>
+</svg>
diff --git a/src/components/Background/Background.scss b/src/components/Background/Background.scss
new file mode 100644
index 00000000..ad920d9b
--- /dev/null
+++ b/src/components/Background/Background.scss
@@ -0,0 +1,9 @@
+.scrumlr-background {
+  background-image: url("../../assets/backgrounds/bg--light.svg");
+  background-repeat: no-repeat;
+  background-size: cover;
+}
+
+[theme="dark"] .scrumlr-background {
+  background-image: url("../../assets/backgrounds/bg--dark.svg");
+}
diff --git a/src/components/Background/Background.tsx b/src/components/Background/Background.tsx
new file mode 100644
index 00000000..b2101533
--- /dev/null
+++ b/src/components/Background/Background.tsx
@@ -0,0 +1,4 @@
+import {FC, PropsWithChildren} from "react";
+import "./Background.scss";
+
+export const Background: FC<PropsWithChildren> = ({children}) => <div className="scrumlr-background">{children}</div>;
diff --git a/src/components/Background/index.ts b/src/components/Background/index.ts
new file mode 100644
index 00000000..bcdae39d
--- /dev/null
+++ b/src/components/Background/index.ts
@@ -0,0 +1,1 @@
+export * from "./Background";
diff --git a/src/components/PassphraseDialog/PassphraseDialog.scss b/src/components/PassphraseDialog/PassphraseDialog.scss
index 21394cd0..6014825b 100644
--- a/src/components/PassphraseDialog/PassphraseDialog.scss
+++ b/src/components/PassphraseDialog/PassphraseDialog.scss
@@ -1,32 +1,185 @@
 @import "constants/style";
 
-.passphrase-dialog__wrapper {
-  display: flex;
-  justify-content: center;
-  align-items: center;
+$color-backlog-blue--light: $color-backlog-blue;
+$color-backlog-blue--dark: #5c8fff;
 
+.passphrase-dialog {
   min-width: 100vw;
   min-height: 100vh;
+  display: grid;
+  place-content: center;
+  padding: 16px;
 }
 
-.passphrase-dialog {
+.passphrase-dialog__logo {
+  position: absolute;
+
+  &:focus {
+    outline: 2px solid $color-backlog-blue--dark;
+  }
+}
+
+.passphrase-dialog__form {
+  background-color: $color-white;
+  border-radius: 24px;
+  box-shadow: 0px 4px 15px 0px rgba($color: $color-backlog-blue--light, $alpha: 0.25);
+  margin: 0 auto;
+
+  padding: $padding--large $padding--medium;
+  gap: $margin--default;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
   width: 100%;
-  max-width: 420px;
+  max-width: 480px;
+}
 
-  padding: $padding--default;
+.form__icon {
+  display: none;
+  color: $color-backlog-blue--light;
+  opacity: 50%;
+  height: $icon--large;
+  width: $icon--large;
+}
+
+.form__label {
   color: $color-black;
+  font-size: $text--md;
+  font-weight: bold;
+  text-align: center;
 }
 
-.passphrase-dialog__form {
-  margin-top: 1.5em;
+.form__input-row {
+  display: flex;
+  gap: $margin--small;
+  min-width: 0;
+  width: 100%;
 }
 
-.passphrase-dialog__submit-button {
-  margin-top: 1em;
+.form__password-input {
+  border: 0;
+  border-radius: $rounded--full;
+  background-color: rgba(0, 87, 255, 0.1);
+  display: flex;
+  align-items: center;
+  min-width: 0;
+  flex-grow: 1;
+  color: $color-black;
+  transition: box-shadow 80ms ease-in-out;
+
+  &:focus-within {
+    outline: 2px solid $color-backlog-blue--dark;
+  }
+  &:hover {
+    box-shadow: 0px 4px 6px -1px rgba($color-backlog-blue--light, 0.25);
+  }
+}
+
+.password-input__input {
+  border: 0;
+  background-color: transparent;
+  height: 100%;
+  padding: 0 $padding--default;
+  font-size: $text--base;
+  min-width: 0;
+  color: inherit;
+  flex-grow: 1;
+
+  &:focus {
+    outline: 0;
+  }
+  &::placeholder {
+    color: inherit;
+    opacity: 0.5;
+  }
+}
+
+.password-input__toggle {
+  height: 32px;
+  width: 32px;
+  background-color: transparent;
+  border: 0;
+  cursor: pointer;
+  margin-right: $margin--small;
+  flex-shrink: 0;
+  padding: 4px;
+  border-radius: $rounded--full;
+  color: inherit;
+
+  &:focus {
+    outline: 2px solid $color-backlog-blue--dark;
+  }
+}
+
+.form__submit-button {
+  height: 40px;
+  width: 40px;
+  padding: 0;
+  border: 0;
+  border-radius: $rounded--full;
+  background-color: $color-backlog-blue--light;
+  color: $color-white;
+  cursor: pointer;
+  flex-shrink: 0;
+  overflow: hidden;
+  transition: box-shadow 80ms ease-in-out;
+  display: grid;
+  place-content: center;
+
+  &:focus {
+    outline: 2px solid #5c8fff;
+  }
+  &:hover:not([aria-disabled="true"]) {
+    box-shadow: 0px 4px 6px -1px rgba($color-backlog-blue--light, 0.25);
+  }
+
+  &[aria-disabled="true"] {
+    cursor: not-allowed;
+    opacity: 0.7;
+  }
+
+  > svg {
+    width: 100%;
+  }
+}
+
+@media (min-width: 460px) {
+  .passphrase-dialog__form {
+    padding: 48px 64px;
+    display: grid;
+    grid-template-columns: auto 1fr;
+    grid-template-rows: auto auto;
+  }
+  .form__icon {
+    display: block;
+    grid-column-start: 1;
+  }
+  .form__label {
+    font-size: $text--lg;
+    text-align: left;
+  }
+  .form__label,
+  .form__input-row {
+    grid-column-start: 2;
+  }
 }
 
 [theme="dark"] {
-  .passphrase-dialog {
+  .passphrase-dialog__form {
+    background-color: $color-dark-mode-note;
+    box-shadow: 0px 6px 9px 0px rgba(27, 30, 38, 0.3);
+  }
+  .form__icon {
+    color: $color-backlog-blue--dark;
+  }
+  .form__label {
     color: $color-white;
   }
+  .form__password-input {
+    background-color: rgba($color-backlog-blue--dark, 0.15);
+    color: $color-white;
+  }
+  .form__submit-button {
+    background-color: $color-backlog-blue--dark;
+  }
 }
diff --git a/src/components/PassphraseDialog/PassphraseDialog.tsx b/src/components/PassphraseDialog/PassphraseDialog.tsx
index cc3cad93..a1bcfcf1 100644
--- a/src/components/PassphraseDialog/PassphraseDialog.tsx
+++ b/src/components/PassphraseDialog/PassphraseDialog.tsx
@@ -1,59 +1,76 @@
-import {FC, useState} from "react";
-import "./PassphraseDialog.scss";
+import {ChangeEvent, FC, FormEvent, MouseEvent, useState} from "react";
+import {ReactComponent as HiddenIcon} from "assets/icon-hidden.svg";
+import {ReactComponent as KeyIcon} from "assets/icon-key.svg";
+import {ReactComponent as VisibleIcon} from "assets/icon-visible.svg";
+import {Background} from "components/Background";
 import {ScrumlrLogo} from "components/ScrumlrLogo";
+import "./PassphraseDialog.scss";
+
+import {ReactComponent as ArrowRightIcon} from "assets/icon-arrow-right.svg";
 import {useTranslation} from "react-i18next";
-import {ReactComponent as VisibleIcon} from "assets/icon-visible.svg";
-import {ReactComponent as HiddenIcon} from "assets/icon-hidden.svg";
-import {TextInputLabel} from "../TextInputLabel";
-import {TextInput} from "../TextInput";
-import {Button} from "../Button";
-import {TextInputAdornment} from "../TextInputAdornment";
 
 export interface PassphraseDialogProps {
   onSubmit: (passphrase: string) => void;
 }
 
-export const PassphraseDialog: FC<PassphraseDialogProps> = ({onSubmit}) => {
+export const PassphraseDialog: FC<PassphraseDialogProps> = ({onSubmit}: PassphraseDialogProps) => {
+  const [passwordVisible, setPasswordVisible] = useState(false);
+  const [passphrase, setPassphrase] = useState("");
   const {t} = useTranslation();
 
-  const [passphrase, setPassphrase] = useState<string>("");
-  const [visiblePassphrase, setVisiblePassphrase] = useState(false);
+  const togglePasswordVisibility = (e: MouseEvent<HTMLButtonElement>) => {
+    e.preventDefault();
+    setPasswordVisible((currentValue) => !currentValue);
+  };
 
-  const handleSubmit = async () => {
-    await onSubmit(passphrase);
+  const changePassphrase = (e: ChangeEvent<HTMLInputElement>) => {
+    setPassphrase(e.target.value);
   };
 
-  const togglePassphraseVisibility = () => {
-    setVisiblePassphrase(!visiblePassphrase);
+  const submitPassphraseForm = (e: FormEvent<HTMLFormElement>) => {
+    if (passphrase.length === 0) {
+      e.preventDefault();
+      return;
+    }
+    onSubmit(passphrase);
   };
 
   return (
-    <div className="passphrase-dialog__wrapper">
+    <Background>
       <div className="passphrase-dialog">
-        <a href="/" aria-label="Homepage">
+        <a className="passphrase-dialog__logo" href="/" aria-label="Homepage">
           <ScrumlrLogo accentColorClassNames={["accent-color--blue", "accent-color--purple", "accent-color--lilac", "accent-color--pink"]} />
         </a>
 
-        <form className="passphrase-dialog__form" onSubmit={handleSubmit}>
-          <TextInputLabel label={t("PassphraseDialog.passphraseInputLabel")} htmlFor="password-dialog__password" />
-          <TextInput
-            id="password-dialog__password"
-            type={visiblePassphrase ? "text" : "password"}
-            rightAdornment={
-              <TextInputAdornment title={t("PassphraseDialog.togglePassphraseVisibility")} onClick={togglePassphraseVisibility}>
-                {visiblePassphrase ? <VisibleIcon /> : <HiddenIcon />}
-              </TextInputAdornment>
-            }
-            value={passphrase}
-            onChange={(e) => setPassphrase(e.target.value)}
-          />
-
-          <Button type="submit" color="primary" className="passphrase-dialog__submit-button" disabled={!passphrase}>
-            {t("PassphraseDialog.submit")}
-          </Button>
+        <form className="passphrase-dialog__form" onSubmit={submitPassphraseForm}>
+          <KeyIcon className="form__icon" />
+          <label className="form__label">{t("PassphraseDialog.label")}</label>
+
+          <div className="form__input-row">
+            <div className="form__password-input">
+              <input
+                className="password-input__input"
+                type={passwordVisible ? "text" : "password"}
+                onChange={changePassphrase}
+                value={passphrase}
+                placeholder={t("PassphraseDialog.inputPlaceholder")}
+              />
+              <button
+                aria-label={t(`PassphraseDialog${passwordVisible ? ".hidePassword" : ".showPassword"}`)}
+                className="password-input__toggle"
+                onClick={togglePasswordVisibility}
+                type="button"
+              >
+                {passwordVisible ? <HiddenIcon /> : <VisibleIcon />}
+              </button>
+            </div>
+            <button aria-disabled={passphrase.length === 0} aria-label={t("PassphraseDialog.submit")} className="form__submit-button" type="submit">
+              <ArrowRightIcon />
+            </button>
+          </div>
         </form>
       </div>
-    </div>
+    </Background>
   );
 };
 


Test patch:
diff --git a/src/components/PassphraseDialog/__tests__/PassphraseDialog.test.tsx b/src/components/PassphraseDialog/__tests__/PassphraseDialog.test.tsx
index 29d2f2c1..0802a225 100644
--- a/src/components/PassphraseDialog/__tests__/PassphraseDialog.test.tsx
+++ b/src/components/PassphraseDialog/__tests__/PassphraseDialog.test.tsx
@@ -1,6 +1,7 @@
-import {fireEvent, waitFor} from "@testing-library/react";
+import {fireEvent} from "@testing-library/react";
 import {render} from "testUtils";
 import {PassphraseDialog} from "../PassphraseDialog";
+import {t} from "i18next";
 
 describe("<PassphraseDialog />", () => {
   test("snapshot test", () => {
@@ -8,46 +9,38 @@ describe("<PassphraseDialog />", () => {
     expect(container.firstChild).toMatchSnapshot();
   });
 
-  describe("passphrase input", () => {
-    test("submit is disabled while passphrase is empty", () => {
-      const {container} = render(<PassphraseDialog onSubmit={jest.fn()} />);
-      expect(container.querySelector(".passphrase-dialog__submit-button")!.hasAttribute("disabled")).toBeTruthy();
-    });
-
-    test("submit is available if passphrase is set", async () => {
-      const {container} = render(<PassphraseDialog onSubmit={jest.fn()} />);
-      fireEvent.change(container.querySelector("#password-dialog__password")!, {target: {value: "1234"}});
-
-      await waitFor(() => {
-        expect(container.querySelector(".passphrase-dialog__submit-button")!.hasAttribute("disabled")).toBeFalsy();
-      });
-    });
+  test("should disable submit button if no passphrase has been entered", () => {
+    const {getByLabelText} = render(<PassphraseDialog onSubmit={jest.fn()} />);
+    const submitButton = getByLabelText(t("PassphraseDialog.submit"));
+    expect(submitButton).toHaveAttribute("aria-disabled", "true");
   });
 
-  describe("visibility toggle", () => {
-    test("passphrase is not visible in default state", () => {
-      const {container} = render(<PassphraseDialog onSubmit={jest.fn()} />);
-      expect(container.querySelector("#password-dialog__password")!.getAttribute("type")).toEqual("password");
-    });
-
-    test("passphrase is visible on visibility toggle", async () => {
-      const {container} = render(<PassphraseDialog onSubmit={jest.fn()} />);
-      fireEvent.click(container.querySelector('button[aria-label="Toggle passphrase visibility"]')!);
-      await waitFor(() => {
-        expect(container.querySelector("#password-dialog__password")!.getAttribute("type")).toEqual("text");
-      });
-    });
+  test("should not submit if no passphrase has been entered", () => {
+    const onSubmit = jest.fn();
+    const {getByLabelText} = render(<PassphraseDialog onSubmit={onSubmit} />);
+    const submitButton = getByLabelText(t("PassphraseDialog.submit"));
+    fireEvent.click(submitButton);
+    expect(onSubmit).not.toHaveBeenCalled();
   });
 
-  describe("manual verification hint", () => {
-    test("manual verification is visible", () => {
-      const {container} = render(<PassphraseDialog onSubmit={jest.fn()} />);
-      expect(container.querySelector(".passphrase-dialog__manual-verification")).toBeNull();
-    });
+  test("should submit with correct passphrase if passphrase has been entered", () => {
+    const onSubmit = jest.fn();
+    const {getByLabelText, getByPlaceholderText} = render(<PassphraseDialog onSubmit={onSubmit} />);
+    const submitButton = getByLabelText(t("PassphraseDialog.submit"));
+    const passwordInput = getByPlaceholderText(t("PassphraseDialog.inputPlaceholder"));
+    fireEvent.change(passwordInput, {target: {value: "Passphrase"}});
+    fireEvent.click(submitButton);
+    expect(onSubmit).toHaveBeenCalled();
+    expect(onSubmit).toHaveBeenCalledWith("Passphrase");
+  });
 
-    test("manual verification is not visible", () => {
-      const {container} = render(<PassphraseDialog onSubmit={jest.fn()} />);
-      expect(container.querySelector(".passphrase-dialog__manual-verification")).toBeDefined();
-    });
+  test("should toggle the password visibility", () => {
+    const {getByDisplayValue, getByLabelText, getByPlaceholderText} = render(<PassphraseDialog onSubmit={jest.fn()} />);
+    const passwordInput = getByPlaceholderText(t("PassphraseDialog.inputPlaceholder"));
+    fireEvent.change(passwordInput, {target: {value: "Passphrase"}});
+    expect(getByDisplayValue("Passphrase")).toHaveProperty("type", "password");
+    let toggleButton = getByLabelText(t("PassphraseDialog.showPassword"));
+    fireEvent.click(toggleButton);
+    expect(getByDisplayValue("Passphrase")).toHaveProperty("type", "text");
   });
 });
diff --git a/src/components/PassphraseDialog/__tests__/__snapshots__/PassphraseDialog.test.tsx.snap b/src/components/PassphraseDialog/__tests__/__snapshots__/PassphraseDialog.test.tsx.snap
index 9b886645..d335c043 100644
--- a/src/components/PassphraseDialog/__tests__/__snapshots__/PassphraseDialog.test.tsx.snap
+++ b/src/components/PassphraseDialog/__tests__/__snapshots__/PassphraseDialog.test.tsx.snap
@@ -2,13 +2,14 @@
 
 exports[`<PassphraseDialog /> snapshot test 1`] = `
 <div
-  class="passphrase-dialog__wrapper"
+  class="scrumlr-background"
 >
   <div
     class="passphrase-dialog"
   >
     <a
       aria-label="Homepage"
+      class="passphrase-dialog__logo"
       href="/"
     >
       <svg
@@ -93,54 +94,49 @@ exports[`<PassphraseDialog /> snapshot test 1`] = `
     <form
       class="passphrase-dialog__form"
     >
+      <svg
+        class="form__icon"
+      >
+        icon-key.svg
+      </svg>
       <label
-        class="text-input-label"
-        for="password-dialog__password"
+        class="form__label"
       >
-        <span
-          class="text-input-label__label"
-        >
-          Enter the passphrase to access this board
-        </span>
+        Enter the password to open this board
       </label>
       <div
-        class="text-input__container"
+        class="form__input-row"
       >
         <div
-          class="text-input__input-wrapper"
+          class="form__password-input"
         >
           <input
-            class="text-input text-input--adornment-right"
-            id="password-dialog__password"
+            class="password-input__input"
+            placeholder="Password"
             type="password"
             value=""
           />
           <button
-            aria-label="Toggle passphrase visibility"
-            class="text-input-adornment text-input__adornment text-input__adornment--right"
-            title="Toggle passphrase visibility"
+            aria-label="Show password"
+            class="password-input__toggle"
             type="button"
           >
             <svg>
-              icon-hidden.svg
+              icon-visible.svg
             </svg>
           </button>
         </div>
-        <div
-          class="text-input__actions"
-        />
-      </div>
-      <button
-        class="button button--primary button--contained button__label--shown passphrase-dialog__submit-button"
-        disabled=""
-        type="submit"
-      >
-        <span
-          class="button__label"
+        <button
+          aria-disabled="true"
+          aria-label="Open board"
+          class="form__submit-button"
+          type="submit"
         >
-          Submit
-        </span>
-      </button>
+          <svg>
+            icon-arrow-right.svg
+          </svg>
+        </button>
+      </div>
     </form>
   </div>
 </div>