diff --git a/VERSION b/VERSION
index 437459cd94c9fa59d82c61c0bc8aa36e293b735e..e70b4523ae7ffe8aa3cac8ecd1b093fba5a98737 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.5.0
+2.6.0
diff --git a/frontend/src/assets/previews/angle_event_left.png b/frontend/src/assets/previews/angle_event_left.png
new file mode 100644
index 0000000000000000000000000000000000000000..6aabcb3a2aba29522187a708258c424d2977462d
Binary files /dev/null and b/frontend/src/assets/previews/angle_event_left.png differ
diff --git a/frontend/src/assets/previews/angle_event_right.png b/frontend/src/assets/previews/angle_event_right.png
new file mode 100644
index 0000000000000000000000000000000000000000..abd170a0feec49d6369601b4b2f328625676280b
Binary files /dev/null and b/frontend/src/assets/previews/angle_event_right.png differ
diff --git a/frontend/src/assets/previews/right_event.png b/frontend/src/assets/previews/right_event.png
new file mode 100644
index 0000000000000000000000000000000000000000..870239374842dbd6c96635709a9fedb891e77514
Binary files /dev/null and b/frontend/src/assets/previews/right_event.png differ
diff --git a/frontend/src/assets/template/angle_event_left/bg_black.png b/frontend/src/assets/template/angle_event_left/bg_black.png
new file mode 100644
index 0000000000000000000000000000000000000000..2f7ffa7a8dc2aa42169f1491439a18997873ad08
Binary files /dev/null and b/frontend/src/assets/template/angle_event_left/bg_black.png differ
diff --git a/frontend/src/assets/template/angle_event_left/bg_white.png b/frontend/src/assets/template/angle_event_left/bg_white.png
new file mode 100644
index 0000000000000000000000000000000000000000..27b875f739d07593783384ceb6d04eadfb9f5063
Binary files /dev/null and b/frontend/src/assets/template/angle_event_left/bg_white.png differ
diff --git a/frontend/src/assets/template/angle_event_right/bg_black.png b/frontend/src/assets/template/angle_event_right/bg_black.png
new file mode 100644
index 0000000000000000000000000000000000000000..293cf58051391a81d6fb56033c448472ad3c1cc7
Binary files /dev/null and b/frontend/src/assets/template/angle_event_right/bg_black.png differ
diff --git a/frontend/src/assets/template/angle_event_right/bg_white.png b/frontend/src/assets/template/angle_event_right/bg_white.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0cd27641481a74033ef60238837781c09e81ce3
Binary files /dev/null and b/frontend/src/assets/template/angle_event_right/bg_white.png differ
diff --git a/frontend/src/assets/template/right_event/bg_black.png b/frontend/src/assets/template/right_event/bg_black.png
new file mode 100644
index 0000000000000000000000000000000000000000..dd2f852308d2dc4e76ec4e41f5110af6f11bba5f
Binary files /dev/null and b/frontend/src/assets/template/right_event/bg_black.png differ
diff --git a/frontend/src/assets/template/right_event/bg_white.png b/frontend/src/assets/template/right_event/bg_white.png
new file mode 100644
index 0000000000000000000000000000000000000000..59ac94f650c0cbcd1065ac4a34ca72645cef72e1
Binary files /dev/null and b/frontend/src/assets/template/right_event/bg_white.png differ
diff --git a/frontend/src/templates.js b/frontend/src/templates.js
index 78c2d7ac0608f9a2af8dad8c21ce656ccfa463e2..b4b84e1bd7545ef8c01687fb86db3027735191b9 100644
--- a/frontend/src/templates.js
+++ b/frontend/src/templates.js
@@ -9,6 +9,9 @@ import twitterBannerImage from "./assets/previews/twitter_banner.png";
 import posterImage from "./assets/previews/poster.png";
 import regionalSuccessImage from "./assets/previews/regional_success.png";
 import baseEventImage from "./assets/previews/base_event.png";
+import rightEventImage from "./assets/previews/right_event.png";
+import angleEventRightImage from "./assets/previews/angle_event_right.png";
+import angleEventLeftImage from "./assets/previews/angle_event_left.png";
 
 const TEMPLATES = {
   basic_photo_banner: {
@@ -105,12 +108,39 @@ const TEMPLATES = {
     },
   },
   base_event: {
-    name: "Událost - pouze text",
+    name: "Událost - pouze text, vprostřed",
     image: baseEventImage,
     path: "/base-event",
     component: () => import("./views/base_event/BaseEvent.vue"),
     meta: {
-      title: "Událost - pouze text",
+      title: "Událost - pouze text, vprostřed",
+    },
+  },
+  right_event: {
+    name: "Událost - pouze text, vpravo",
+    image: rightEventImage,
+    path: "/right-event",
+    component: () => import("./views/right_event/RightEvent.vue"),
+    meta: {
+      title: "Událost - pouze text, vpravo",
+    },
+  },
+  angle_event_right: {
+    name: "Událost - pruh pod úhlem, vpravo",
+    image: angleEventRightImage,
+    path: "/angle-event-right",
+    component: () => import("./views/angle_event_right/AngleEventRight.vue"),
+    meta: {
+      title: "Událost - pruh pod úhlem, vpravo",
+    },
+  },
+  angle_event_left: {
+    name: "Událost - pruh pod úhlem, vlevo",
+    image: angleEventLeftImage,
+    path: "/angle-event-left",
+    component: () => import("./views/angle_event_left/AngleEventLeft.vue"),
+    meta: {
+      title: "Událost - pruh pod úhlem, vlevo",
     },
   },
 };
diff --git a/frontend/src/views/angle_event_left/AngleEventLeft.vue b/frontend/src/views/angle_event_left/AngleEventLeft.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b5c4a7ababf5e74bfdab83bed7a8c14473776a98
--- /dev/null
+++ b/frontend/src/views/angle_event_left/AngleEventLeft.vue
@@ -0,0 +1,171 @@
+<script setup>
+import { watch, ref } from "vue";
+
+import COLORS from "../../colors";
+import TEMPLATES from "../../templates";
+import DEFAULT_CONTRACTOR from "../../contractors";
+import {
+  loadFonts,
+  loadCanvasStorage,
+  setCanvasStorage,
+  updateAutoRedrawStorage,
+} from "../../utils";
+
+import Canvas from "../../components/canvas/Canvas.vue";
+import redraw from "./canvas";
+
+import Navbar from "../../components/Navbar.vue";
+import MainContainer from "../../components/MainContainer.vue";
+import LongTextInput from "../../components/inputs/text/LongTextInput.vue";
+import ShortTextInput from "../../components/inputs/text/ShortTextInput.vue";
+import MultipleColorPicker from "../../components/inputs/colors/MultipleColorPicker.vue";
+import ReloadButton from "../../components/reload/ReloadButton.vue";
+import AutoReloadCheckbox from "../../components/reload/AutoReloadCheckbox.vue";
+</script>
+
+<script>
+await loadFonts(["12px Bebas Neue", "12px Roboto Condensed"]);
+
+export default {
+  components: {
+    Canvas,
+    Navbar,
+    MainContainer,
+    LongTextInput,
+    ShortTextInput,
+    MultipleColorPicker,
+    AutoReloadCheckbox,
+    ReloadButton,
+  },
+  data() {
+    const predefinedColors = {
+      blackBackground: {
+        name: "Černé pozadí, bílý text",
+        colors: {
+          background: COLORS.black,
+          mainText: COLORS.white,
+          highlightedText: COLORS.black,
+          highlight: COLORS.yellow1,
+          contractedByText: COLORS.gray1,
+        },
+      },
+      whiteBackground: {
+        name: "Bílé pozadí, černý text",
+        colors: {
+          background: COLORS.white,
+          mainText: COLORS.black,
+          highlightedText: COLORS.black,
+          highlight: COLORS.yellow1,
+          contractedByText: COLORS.gray1,
+        },
+      },
+    };
+
+    return {
+      mainText: null,
+      contractedBy: DEFAULT_CONTRACTOR,
+      gradientHeightMultiplier: 1,
+      colorLabels: {
+        background: "Pozadí",
+      },
+      predefinedColors: predefinedColors,
+      colors: predefinedColors.blackBackground.colors,
+      autoRedraw: false,
+    };
+  },
+  async created() {
+    await loadCanvasStorage(this);
+  },
+  methods: {
+    async reloadCanvasProperties() {
+      const canvasProperties = {
+        mainText: this.mainText,
+        contractedBy: this.contractedBy,
+        colors: this.colors,
+      };
+
+      await this.$refs.canvas.redraw(canvasProperties);
+
+      delete canvasProperties.colors;
+      setCanvasStorage(canvasProperties);
+    },
+  },
+  mounted() {
+    this.$watch(
+      (vm) => [vm.mainText, vm.contractedBy, vm.colors],
+      async (value) => {
+        if (this.autoRedraw) {
+          await this.reloadCanvasProperties();
+        }
+      },
+      {
+        immediate: true,
+        deep: true,
+      },
+    );
+
+    this.$watch(
+      (vm) => [vm.autoRedraw],
+      async (value) => {
+        updateAutoRedrawStorage(this.autoRedraw);
+
+        if (this.autoRedraw) {
+          await this.reloadCanvasProperties();
+        }
+      },
+    );
+  },
+};
+</script>
+
+<template>
+  <header>
+    <Navbar :defaultTemplate="TEMPLATES.angle_event_left"></Navbar>
+  </header>
+  <main>
+    <MainContainer>
+      <template v-slot:left>
+        <Canvas
+          ref="canvas"
+          :redrawFunction="redraw"
+          width="1702"
+          height="630"
+        />
+      </template>
+
+      <template v-slot:right>
+        <ReloadButton :parentRefs="$refs" @click="reloadCanvasProperties" />
+        <AutoReloadCheckbox v-model="autoRedraw" />
+        <LongTextInput
+          name="Text"
+          v-model="mainText"
+          :important="true"
+          :highlightable="true"
+          zIndex="9"
+        />
+
+        <MultipleColorPicker
+          name="Barvy"
+          v-model="colors"
+          :important="false"
+          :colorLabels="colorLabels"
+          :predefinedColors="predefinedColors"
+          :defaultPredefinedColors="predefinedColors.blackBackground"
+          zIndex="5"
+        ></MultipleColorPicker>
+
+        <ShortTextInput
+          name="Zadavatel a zpracovatel"
+          v-model="contractedBy"
+          :defaultValue="DEFAULT_CONTRACTOR"
+          :important="false"
+          zIndex="4"
+        />
+      </template>
+    </MainContainer>
+  </main>
+</template>
+
+<style>
+@import "vue-select/dist/vue-select.css";
+</style>
diff --git a/frontend/src/views/angle_event_left/canvas.js b/frontend/src/views/angle_event_left/canvas.js
new file mode 100644
index 0000000000000000000000000000000000000000..3e3268917e6a8bccd4d6992de6cdba4cff5401b7
--- /dev/null
+++ b/frontend/src/views/angle_event_left/canvas.js
@@ -0,0 +1,141 @@
+import { fabric } from "fabric";
+import {
+  clearObjects,
+  sortObjects,
+  transformHighlightedText,
+  checkTextBoxHeight,
+} from "../../components/canvas/utils";
+import COLORS from "../../colors";
+import { PaddedHighlightingTextbox } from "../../components/canvas/textbox";
+
+import bgImageSourceBlack from "../../assets/template/angle_event_left/bg_black.png";
+import bgImageSourceWhite from "../../assets/template/angle_event_left/bg_white.png";
+
+let mainImage = null;
+let contractedByTextbox = null;
+let previousColor = null;
+let mainTextBox = null;
+
+const redraw = async (canvas, options) => {
+  clearObjects([contractedByTextbox, mainTextBox], canvas);
+
+  const contractedByTextSize = Math.ceil(canvas.height * 0.035);
+  const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9);
+  const contractedByTextBottomMargin = Math.ceil(canvas.width * 0.02);
+  const contractedByTextSideMargin = Math.ceil(canvas.width * 0.03);
+
+  const textMarginLeft = Math.ceil(canvas.width * 0.54);
+  const textMarginRight = Math.ceil(canvas.width * 0.15);
+
+  let mainTextMarginTop = Math.ceil(canvas.height * 0.185);
+  const mainTextBackgroundMarginTop = Math.ceil(canvas.height * 0.14);
+  const mainTextSize = Math.ceil(canvas.height * 0.22);
+  const mainTextHeightLimit = Math.ceil(mainTextSize * 2.2);
+  const mainTextLineHeight = 0.85;
+
+  canvas.preserveObjectStacking = true;
+
+  /* BEGIN Main image render */
+
+  if (previousColor !== options.colors.background || mainImage === null) {
+    if (mainImage !== null) {
+      canvas.remove(mainImage);
+    }
+
+    const image = new Image();
+
+    const imageLoadPromise = new Promise((resolve) => {
+      image.onload = () => {
+        resolve();
+      };
+
+      if (options.colors.background.value === COLORS.black.value) {
+        image.src = bgImageSourceBlack;
+      } else {
+        image.src = bgImageSourceWhite;
+      }
+    });
+    await imageLoadPromise;
+
+    mainImage = new fabric.Image(image, {
+      left: 0,
+      top: 0,
+      zIndex: 0,
+      selectable: false,
+    });
+
+    mainImage.scaleToWidth(canvas.width);
+
+    canvas.add(mainImage);
+
+    previousColor = options.colors.background;
+  }
+
+  /* END Main image render */
+
+  if (options.mainText !== null) {
+    /* BEGIN Name text render */
+
+    const mainTextWidth = canvas.width - textMarginLeft - textMarginRight;
+
+    const highlightedData = transformHighlightedText(
+      options.mainText,
+      mainTextSize,
+      mainTextWidth,
+      "Bebas Neue",
+      options.colors.highlight.value,
+      options.colors.highlightedText.value,
+      { padWhenDiacritics: true },
+    );
+
+    mainTextBox = new PaddedHighlightingTextbox(highlightedData.text, {
+      width: canvas.width,
+      left: textMarginLeft,
+      textAlign: "left",
+      fontFamily: "Bebas Neue",
+      fontSize: mainTextSize,
+      lineHeight: mainTextLineHeight,
+      fill: options.colors.mainText.value,
+      styles: highlightedData.styles,
+      selectable: false,
+      highlightPadding: canvas.height * 0.003,
+      zIndex: 10,
+    });
+
+    checkTextBoxHeight(mainTextBox, 3);
+
+    canvas.add(mainTextBox);
+
+    mainTextBox.top = mainTextMarginTop - highlightedData.paddingBottom;
+
+    canvas.renderAll();
+
+    /* END Main text render */
+  }
+
+  /* BEGIN Contracted by render */
+
+  if (options.contractedBy !== null) {
+    contractedByTextbox = new fabric.Textbox(options.contractedBy, {
+      left: contractedByTextSideMargin,
+      top: canvas.height - contractedByTextBottomMargin - contractedByTextSize,
+      width: contractedByTextMaxWidth,
+      fontFamily: "Roboto Condensed",
+      fontSize: contractedByTextSize,
+      textAlign: "left",
+      fill: options.colors.contractedByText.value,
+      selectable: false,
+      zIndex: 10,
+    });
+
+    checkTextBoxHeight(contractedByTextbox, 1);
+
+    canvas.add(contractedByTextbox);
+  }
+
+  /* END Contracted by render */
+
+  sortObjects(canvas);
+};
+
+export default redraw;
diff --git a/frontend/src/views/angle_event_right/AngleEventRight.vue b/frontend/src/views/angle_event_right/AngleEventRight.vue
new file mode 100644
index 0000000000000000000000000000000000000000..a63a96869d42c643c8d0f0ef0cb65b322a7c63e1
--- /dev/null
+++ b/frontend/src/views/angle_event_right/AngleEventRight.vue
@@ -0,0 +1,171 @@
+<script setup>
+import { watch, ref } from "vue";
+
+import COLORS from "../../colors";
+import TEMPLATES from "../../templates";
+import DEFAULT_CONTRACTOR from "../../contractors";
+import {
+  loadFonts,
+  loadCanvasStorage,
+  setCanvasStorage,
+  updateAutoRedrawStorage,
+} from "../../utils";
+
+import Canvas from "../../components/canvas/Canvas.vue";
+import redraw from "./canvas";
+
+import Navbar from "../../components/Navbar.vue";
+import MainContainer from "../../components/MainContainer.vue";
+import LongTextInput from "../../components/inputs/text/LongTextInput.vue";
+import ShortTextInput from "../../components/inputs/text/ShortTextInput.vue";
+import MultipleColorPicker from "../../components/inputs/colors/MultipleColorPicker.vue";
+import ReloadButton from "../../components/reload/ReloadButton.vue";
+import AutoReloadCheckbox from "../../components/reload/AutoReloadCheckbox.vue";
+</script>
+
+<script>
+await loadFonts(["12px Bebas Neue", "12px Roboto Condensed"]);
+
+export default {
+  components: {
+    Canvas,
+    Navbar,
+    MainContainer,
+    LongTextInput,
+    ShortTextInput,
+    MultipleColorPicker,
+    AutoReloadCheckbox,
+    ReloadButton,
+  },
+  data() {
+    const predefinedColors = {
+      blackBackground: {
+        name: "Černé pozadí, bílý text",
+        colors: {
+          background: COLORS.black,
+          mainText: COLORS.white,
+          highlightedText: COLORS.black,
+          highlight: COLORS.yellow1,
+          contractedByText: COLORS.gray1,
+        },
+      },
+      whiteBackground: {
+        name: "Bílé pozadí, černý text",
+        colors: {
+          background: COLORS.white,
+          mainText: COLORS.black,
+          highlightedText: COLORS.black,
+          highlight: COLORS.yellow1,
+          contractedByText: COLORS.gray1,
+        },
+      },
+    };
+
+    return {
+      mainText: null,
+      contractedBy: DEFAULT_CONTRACTOR,
+      gradientHeightMultiplier: 1,
+      colorLabels: {
+        background: "Pozadí",
+      },
+      predefinedColors: predefinedColors,
+      colors: predefinedColors.blackBackground.colors,
+      autoRedraw: false,
+    };
+  },
+  async created() {
+    await loadCanvasStorage(this);
+  },
+  methods: {
+    async reloadCanvasProperties() {
+      const canvasProperties = {
+        mainText: this.mainText,
+        contractedBy: this.contractedBy,
+        colors: this.colors,
+      };
+
+      await this.$refs.canvas.redraw(canvasProperties);
+
+      delete canvasProperties.colors;
+      setCanvasStorage(canvasProperties);
+    },
+  },
+  mounted() {
+    this.$watch(
+      (vm) => [vm.mainText, vm.contractedBy, vm.colors],
+      async (value) => {
+        if (this.autoRedraw) {
+          await this.reloadCanvasProperties();
+        }
+      },
+      {
+        immediate: true,
+        deep: true,
+      },
+    );
+
+    this.$watch(
+      (vm) => [vm.autoRedraw],
+      async (value) => {
+        updateAutoRedrawStorage(this.autoRedraw);
+
+        if (this.autoRedraw) {
+          await this.reloadCanvasProperties();
+        }
+      },
+    );
+  },
+};
+</script>
+
+<template>
+  <header>
+    <Navbar :defaultTemplate="TEMPLATES.angle_event_right"></Navbar>
+  </header>
+  <main>
+    <MainContainer>
+      <template v-slot:left>
+        <Canvas
+          ref="canvas"
+          :redrawFunction="redraw"
+          width="1702"
+          height="630"
+        />
+      </template>
+
+      <template v-slot:right>
+        <ReloadButton :parentRefs="$refs" @click="reloadCanvasProperties" />
+        <AutoReloadCheckbox v-model="autoRedraw" />
+        <LongTextInput
+          name="Text"
+          v-model="mainText"
+          :important="true"
+          :highlightable="true"
+          zIndex="9"
+        />
+
+        <MultipleColorPicker
+          name="Barvy"
+          v-model="colors"
+          :important="false"
+          :colorLabels="colorLabels"
+          :predefinedColors="predefinedColors"
+          :defaultPredefinedColors="predefinedColors.blackBackground"
+          zIndex="5"
+        ></MultipleColorPicker>
+
+        <ShortTextInput
+          name="Zadavatel a zpracovatel"
+          v-model="contractedBy"
+          :defaultValue="DEFAULT_CONTRACTOR"
+          :important="false"
+          zIndex="4"
+        />
+      </template>
+    </MainContainer>
+  </main>
+</template>
+
+<style>
+@import "vue-select/dist/vue-select.css";
+</style>
diff --git a/frontend/src/views/angle_event_right/canvas.js b/frontend/src/views/angle_event_right/canvas.js
new file mode 100644
index 0000000000000000000000000000000000000000..c4eba342c747d75223cefa65ee2599532359aa48
--- /dev/null
+++ b/frontend/src/views/angle_event_right/canvas.js
@@ -0,0 +1,141 @@
+import { fabric } from "fabric";
+import {
+  clearObjects,
+  sortObjects,
+  transformHighlightedText,
+  checkTextBoxHeight,
+} from "../../components/canvas/utils";
+import COLORS from "../../colors";
+import { PaddedHighlightingTextbox } from "../../components/canvas/textbox";
+
+import bgImageSourceBlack from "../../assets/template/angle_event_right/bg_black.png";
+import bgImageSourceWhite from "../../assets/template/angle_event_right/bg_white.png";
+
+let mainImage = null;
+let contractedByTextbox = null;
+let previousColor = null;
+let mainTextBox = null;
+
+const redraw = async (canvas, options) => {
+  clearObjects([contractedByTextbox, mainTextBox], canvas);
+
+  const contractedByTextSize = Math.ceil(canvas.height * 0.035);
+  const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9);
+  const contractedByTextBottomMargin = Math.ceil(canvas.width * 0.02);
+  const contractedByTextSideMargin = Math.ceil(canvas.width * 0.03);
+
+  const textMarginLeft = Math.ceil(canvas.width * 0.52);
+  const textMarginRight = Math.ceil(canvas.width * 0.1);
+
+  let mainTextMarginTop = Math.ceil(canvas.height * 0.185);
+  const mainTextBackgroundMarginTop = Math.ceil(canvas.height * 0.14);
+  const mainTextSize = Math.ceil(canvas.height * 0.22);
+  const mainTextHeightLimit = Math.ceil(mainTextSize * 2.2);
+  const mainTextLineHeight = 0.85;
+
+  canvas.preserveObjectStacking = true;
+
+  /* BEGIN Main image render */
+
+  if (previousColor !== options.colors.background || mainImage === null) {
+    if (mainImage !== null) {
+      canvas.remove(mainImage);
+    }
+
+    const image = new Image();
+
+    const imageLoadPromise = new Promise((resolve) => {
+      image.onload = () => {
+        resolve();
+      };
+
+      if (options.colors.background.value === COLORS.black.value) {
+        image.src = bgImageSourceBlack;
+      } else {
+        image.src = bgImageSourceWhite;
+      }
+    });
+    await imageLoadPromise;
+
+    mainImage = new fabric.Image(image, {
+      left: 0,
+      top: 0,
+      zIndex: 0,
+      selectable: false,
+    });
+
+    mainImage.scaleToWidth(canvas.width);
+
+    canvas.add(mainImage);
+
+    previousColor = options.colors.background;
+  }
+
+  /* END Main image render */
+
+  if (options.mainText !== null) {
+    /* BEGIN Name text render */
+
+    const mainTextWidth = canvas.width - textMarginLeft - textMarginRight;
+
+    const highlightedData = transformHighlightedText(
+      options.mainText,
+      mainTextSize,
+      mainTextWidth,
+      "Bebas Neue",
+      options.colors.highlight.value,
+      options.colors.highlightedText.value,
+      { padWhenDiacritics: true },
+    );
+
+    mainTextBox = new PaddedHighlightingTextbox(highlightedData.text, {
+      width: canvas.width,
+      left: textMarginLeft,
+      textAlign: "left",
+      fontFamily: "Bebas Neue",
+      fontSize: mainTextSize,
+      lineHeight: mainTextLineHeight,
+      fill: options.colors.mainText.value,
+      styles: highlightedData.styles,
+      selectable: false,
+      highlightPadding: canvas.height * 0.003,
+      zIndex: 10,
+    });
+
+    checkTextBoxHeight(mainTextBox, 3);
+
+    canvas.add(mainTextBox);
+
+    mainTextBox.top = mainTextMarginTop - highlightedData.paddingBottom;
+
+    canvas.renderAll();
+
+    /* END Main text render */
+  }
+
+  /* BEGIN Contracted by render */
+
+  if (options.contractedBy !== null) {
+    contractedByTextbox = new fabric.Textbox(options.contractedBy, {
+      left: contractedByTextSideMargin,
+      top: canvas.height - contractedByTextBottomMargin - contractedByTextSize,
+      width: contractedByTextMaxWidth,
+      fontFamily: "Roboto Condensed",
+      fontSize: contractedByTextSize,
+      textAlign: "left",
+      fill: options.colors.contractedByText.value,
+      selectable: false,
+      zIndex: 10,
+    });
+
+    checkTextBoxHeight(contractedByTextbox, 1);
+
+    canvas.add(contractedByTextbox);
+  }
+
+  /* END Contracted by render */
+
+  sortObjects(canvas);
+};
+
+export default redraw;
diff --git a/frontend/src/views/base_event/BaseEvent.vue b/frontend/src/views/base_event/BaseEvent.vue
index 2e08d2890301fdff3f99f2960dd86c0f8f347da5..44a4ee8c6a4abd575cb95a64625ef6b4a93c6818 100644
--- a/frontend/src/views/base_event/BaseEvent.vue
+++ b/frontend/src/views/base_event/BaseEvent.vue
@@ -150,7 +150,7 @@ export default {
           :important="false"
           :colorLabels="colorLabels"
           :predefinedColors="predefinedColors"
-          :defaultPredefinedColors="predefinedColors.base"
+          :defaultPredefinedColors="predefinedColors.blackBackground"
           zIndex="5"
         ></MultipleColorPicker>
 
diff --git a/frontend/src/views/base_event/canvas.js b/frontend/src/views/base_event/canvas.js
index cf40ff72d14e63a935824483ff28b63276f165b2..b6adcaa6bbd9addddfd2c1738ab84d3502c96e99 100644
--- a/frontend/src/views/base_event/canvas.js
+++ b/frontend/src/views/base_event/canvas.js
@@ -37,7 +37,7 @@ const redraw = async (canvas, options) => {
 
   /* BEGIN Main image render */
 
-  if (previousColor !== options.colors.background) {
+  if (previousColor !== options.colors.background || mainImage === null) {
     if (mainImage !== null) {
       canvas.remove(mainImage);
     }
diff --git a/frontend/src/views/right_event/RightEvent.vue b/frontend/src/views/right_event/RightEvent.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1a47a42c34705e6728664caa017d13e60e021bbf
--- /dev/null
+++ b/frontend/src/views/right_event/RightEvent.vue
@@ -0,0 +1,171 @@
+<script setup>
+import { watch, ref } from "vue";
+
+import COLORS from "../../colors";
+import TEMPLATES from "../../templates";
+import DEFAULT_CONTRACTOR from "../../contractors";
+import {
+  loadFonts,
+  loadCanvasStorage,
+  setCanvasStorage,
+  updateAutoRedrawStorage,
+} from "../../utils";
+
+import Canvas from "../../components/canvas/Canvas.vue";
+import redraw from "./canvas";
+
+import Navbar from "../../components/Navbar.vue";
+import MainContainer from "../../components/MainContainer.vue";
+import LongTextInput from "../../components/inputs/text/LongTextInput.vue";
+import ShortTextInput from "../../components/inputs/text/ShortTextInput.vue";
+import MultipleColorPicker from "../../components/inputs/colors/MultipleColorPicker.vue";
+import ReloadButton from "../../components/reload/ReloadButton.vue";
+import AutoReloadCheckbox from "../../components/reload/AutoReloadCheckbox.vue";
+</script>
+
+<script>
+await loadFonts(["12px Bebas Neue", "12px Roboto Condensed"]);
+
+export default {
+  components: {
+    Canvas,
+    Navbar,
+    MainContainer,
+    LongTextInput,
+    ShortTextInput,
+    MultipleColorPicker,
+    AutoReloadCheckbox,
+    ReloadButton,
+  },
+  data() {
+    const predefinedColors = {
+      blackBackground: {
+        name: "Černé pozadí, bílý text",
+        colors: {
+          background: COLORS.black,
+          mainText: COLORS.white,
+          highlightedText: COLORS.black,
+          highlight: COLORS.yellow1,
+          contractedByText: COLORS.gray1,
+        },
+      },
+      whiteBackground: {
+        name: "Bílé pozadí, černý text",
+        colors: {
+          background: COLORS.white,
+          mainText: COLORS.black,
+          highlightedText: COLORS.black,
+          highlight: COLORS.yellow1,
+          contractedByText: COLORS.gray1,
+        },
+      },
+    };
+
+    return {
+      mainText: null,
+      contractedBy: DEFAULT_CONTRACTOR,
+      gradientHeightMultiplier: 1,
+      colorLabels: {
+        background: "Pozadí",
+      },
+      predefinedColors: predefinedColors,
+      colors: predefinedColors.blackBackground.colors,
+      autoRedraw: false,
+    };
+  },
+  async created() {
+    await loadCanvasStorage(this);
+  },
+  methods: {
+    async reloadCanvasProperties() {
+      const canvasProperties = {
+        mainText: this.mainText,
+        contractedBy: this.contractedBy,
+        colors: this.colors,
+      };
+
+      await this.$refs.canvas.redraw(canvasProperties);
+
+      delete canvasProperties.colors;
+      setCanvasStorage(canvasProperties);
+    },
+  },
+  mounted() {
+    this.$watch(
+      (vm) => [vm.mainText, vm.contractedBy, vm.colors],
+      async (value) => {
+        if (this.autoRedraw) {
+          await this.reloadCanvasProperties();
+        }
+      },
+      {
+        immediate: true,
+        deep: true,
+      },
+    );
+
+    this.$watch(
+      (vm) => [vm.autoRedraw],
+      async (value) => {
+        updateAutoRedrawStorage(this.autoRedraw);
+
+        if (this.autoRedraw) {
+          await this.reloadCanvasProperties();
+        }
+      },
+    );
+  },
+};
+</script>
+
+<template>
+  <header>
+    <Navbar :defaultTemplate="TEMPLATES.right_event"></Navbar>
+  </header>
+  <main>
+    <MainContainer>
+      <template v-slot:left>
+        <Canvas
+          ref="canvas"
+          :redrawFunction="redraw"
+          width="1702"
+          height="630"
+        />
+      </template>
+
+      <template v-slot:right>
+        <ReloadButton :parentRefs="$refs" @click="reloadCanvasProperties" />
+        <AutoReloadCheckbox v-model="autoRedraw" />
+        <LongTextInput
+          name="Text"
+          v-model="mainText"
+          :important="true"
+          :highlightable="true"
+          zIndex="9"
+        />
+
+        <MultipleColorPicker
+          name="Barvy"
+          v-model="colors"
+          :important="false"
+          :colorLabels="colorLabels"
+          :predefinedColors="predefinedColors"
+          :defaultPredefinedColors="predefinedColors.blackBackground"
+          zIndex="5"
+        ></MultipleColorPicker>
+
+        <ShortTextInput
+          name="Zadavatel a zpracovatel"
+          v-model="contractedBy"
+          :defaultValue="DEFAULT_CONTRACTOR"
+          :important="false"
+          zIndex="4"
+        />
+      </template>
+    </MainContainer>
+  </main>
+</template>
+
+<style>
+@import "vue-select/dist/vue-select.css";
+</style>
diff --git a/frontend/src/views/right_event/canvas.js b/frontend/src/views/right_event/canvas.js
new file mode 100644
index 0000000000000000000000000000000000000000..d6c6998e4a4002d7f58f5d0bd040ed8aef8be425
--- /dev/null
+++ b/frontend/src/views/right_event/canvas.js
@@ -0,0 +1,148 @@
+import { fabric } from "fabric";
+import {
+  clearObjects,
+  sortObjects,
+  transformHighlightedText,
+  checkTextBoxHeight,
+} from "../../components/canvas/utils";
+import COLORS from "../../colors";
+import { PaddedHighlightingTextbox } from "../../components/canvas/textbox";
+
+import bgImageSourceBlack from "../../assets/template/right_event/bg_black.png";
+import bgImageSourceWhite from "../../assets/template/right_event/bg_white.png";
+
+let mainImage = null;
+let contractedByTextbox = null;
+let previousColor = null;
+let mainTextBox = null;
+
+const redraw = async (canvas, options) => {
+  clearObjects([contractedByTextbox, mainTextBox], canvas);
+
+  const contractedByTextSize = Math.ceil(canvas.height * 0.035);
+  const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9);
+  const contractedByTextBottomMargin = Math.ceil(canvas.width * 0.02);
+  const contractedByTextSideMargin = Math.ceil(canvas.width * 0.03);
+
+  const textMarginLeft = Math.ceil(canvas.width * 0.53);
+  const textMarginRight = Math.ceil(canvas.width * 0.1);
+
+  let mainTextMarginBottom = Math.ceil(canvas.height * 0.36);
+  const mainTextBackgroundMarginTop = Math.ceil(canvas.height * 0.14);
+  const mainTextSize = Math.ceil(canvas.height * 0.13);
+  const mainTextHeightLimit = Math.ceil(mainTextSize * 2.2);
+  const mainTextLineHeight = 0.85;
+
+  canvas.preserveObjectStacking = true;
+
+  /* BEGIN Main image render */
+
+  if (previousColor !== options.colors.background || mainImage === null) {
+    if (mainImage !== null) {
+      canvas.remove(mainImage);
+    }
+
+    const image = new Image();
+
+    const imageLoadPromise = new Promise((resolve) => {
+      image.onload = () => {
+        resolve();
+      };
+
+      if (options.colors.background.value === COLORS.black.value) {
+        image.src = bgImageSourceBlack;
+      } else {
+        image.src = bgImageSourceWhite;
+      }
+    });
+    await imageLoadPromise;
+
+    mainImage = new fabric.Image(image, {
+      left: 0,
+      top: 0,
+      zIndex: 0,
+      selectable: false,
+    });
+
+    mainImage.scaleToWidth(canvas.width);
+
+    canvas.add(mainImage);
+
+    previousColor = options.colors.background;
+  }
+
+  /* END Main image render */
+
+  if (options.mainText !== null) {
+    /* BEGIN Name text render */
+
+    const mainTextWidth = canvas.width - textMarginLeft - textMarginRight;
+
+    const highlightedData = transformHighlightedText(
+      options.mainText,
+      mainTextSize,
+      mainTextWidth,
+      "Bebas Neue",
+      options.colors.highlight.value,
+      options.colors.highlightedText.value,
+      { padWhenDiacritics: true },
+    );
+
+    mainTextBox = new PaddedHighlightingTextbox(highlightedData.text, {
+      width: canvas.width,
+      left: textMarginLeft,
+      textAlign: "left",
+      fontFamily: "Bebas Neue",
+      fontSize: mainTextSize,
+      lineHeight: mainTextLineHeight,
+      fill: options.colors.mainText.value,
+      styles: highlightedData.styles,
+      selectable: false,
+      highlightPadding: canvas.height * 0.003,
+      zIndex: 10,
+    });
+
+    checkTextBoxHeight(mainTextBox, 2);
+
+    canvas.add(mainTextBox);
+
+    let mainTextBoxTop =
+      canvas.height - mainTextBox.height - mainTextMarginBottom;
+
+    if (mainTextBox.textLines.length === 1) {
+      mainTextBoxTop -= mainTextSize / 2;
+    }
+
+    mainTextBox.top = mainTextBoxTop - highlightedData.paddingBottom;
+
+    canvas.renderAll();
+
+    /* END Main text render */
+  }
+
+  /* BEGIN Contracted by render */
+
+  if (options.contractedBy !== null) {
+    contractedByTextbox = new fabric.Textbox(options.contractedBy, {
+      left: contractedByTextSideMargin,
+      top: canvas.height - contractedByTextBottomMargin - contractedByTextSize,
+      width: contractedByTextMaxWidth,
+      fontFamily: "Roboto Condensed",
+      fontSize: contractedByTextSize,
+      textAlign: "left",
+      fill: options.colors.contractedByText.value,
+      selectable: false,
+      zIndex: 10,
+    });
+
+    checkTextBoxHeight(contractedByTextbox, 1);
+
+    canvas.add(contractedByTextbox);
+  }
+
+  /* END Contracted by render */
+
+  sortObjects(canvas);
+};
+
+export default redraw;