diff --git a/base.scss b/base.scss
index 1c94a6f29..5e584d9d1 100644
--- a/base.scss
+++ b/base.scss
@@ -144,6 +144,7 @@
@import "./src/stories/Library/filtered-event-list/filtered-event-list";
@import "./src/stories/Library/event-list-stacked/event-list-stacked";
@import "./src/stories/Library/material-search/material-search";
+@import "./src/stories/Library/banner/banner";
// Autosuggest block styling needs to be loaded before the rest of the scss for autosuggest
@import "./src/stories/Blocks/autosuggest/autosuggest";
diff --git a/public/icons/basic/icon-underlined.svg b/public/icons/basic/icon-underlined.svg
new file mode 100644
index 000000000..8352ab299
--- /dev/null
+++ b/public/icons/basic/icon-underlined.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/stories/Library/banner/Banner.stories.tsx b/src/stories/Library/banner/Banner.stories.tsx
new file mode 100644
index 000000000..4f5ac8a6b
--- /dev/null
+++ b/src/stories/Library/banner/Banner.stories.tsx
@@ -0,0 +1,72 @@
+import { ComponentMeta, ComponentStory } from "@storybook/react";
+import { withDesign } from "storybook-addon-designs";
+import Banner from "./Banner";
+
+export default {
+ title: "Library / Banner",
+ component: Banner,
+ decorators: [withDesign],
+ parameters: {
+ design: {
+ type: "figma",
+ url: "https://www.figma.com/design/Zx9GrkFA3l4ISvyZD2q0Qi/Designsystem?node-id=446-6957&t=IWAOniAbcjV2y2Hf-4",
+ },
+ },
+ argTypes: {
+ imageSrc: {
+ defaultValue: "images/campaign_cover.jpg",
+ control: { type: "text" },
+ },
+ title: {
+ name: "Title",
+ defaultValue: "Hvad skal jeg høre?",
+ control: { type: "text" },
+ },
+ description: {
+ name: "Description",
+ defaultValue:
+ "Om du er dedikeret musiknørd eller moderat musikinteresseret, så er dette siden til dig. Her kan du finde anbefalinger, digitale musikmagasiner, nyheder, musiklitteratur og meget mere.",
+ control: { type: "text" },
+ },
+ link: {
+ name: "Link",
+ defaultValue: "#",
+ control: { type: "text" },
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => ;
+
+export const Default = Template.bind({});
+
+export const NoImage = Template.bind({});
+NoImage.args = {
+ imageSrc: undefined,
+};
+
+export const NoImageOnlyTitle = Template.bind({});
+NoImageOnlyTitle.args = {
+ title: "Title uden billede",
+ imageSrc: undefined,
+ description: undefined,
+};
+
+export const NoImageOnlyDescription = Template.bind({});
+NoImageOnlyDescription.args = {
+ title: undefined,
+ imageSrc: undefined,
+ description:
+ "Om du er dedikeret musiknørd eller moderat musikinteresseret, så er dette siden til dig. Her kan du finde anbefalinger, digitale musikmagasiner, nyheder, musiklitteratur og meget mere.",
+};
+
+export const WithImageOnlyTitle = Template.bind({});
+WithImageOnlyTitle.args = {
+ title: "Banner uden beskrivelse",
+ description: undefined,
+};
+export const WithImageOnlyDescription = Template.bind({});
+WithImageOnlyDescription.args = {
+ description:
+ "Om du er dedikeret musiknørd eller moderat musikinteresseret, så er dette siden til dig. Her kan du finde anbefalinger, digitale musikmagasiner, nyheder, musiklitteratur og meget mere.",
+};
diff --git a/src/stories/Library/banner/Banner.tsx b/src/stories/Library/banner/Banner.tsx
new file mode 100644
index 000000000..9bdb2787e
--- /dev/null
+++ b/src/stories/Library/banner/Banner.tsx
@@ -0,0 +1,46 @@
+import clsx from "clsx";
+import { FC, ReactNode } from "react";
+import { ReactComponent as ArrowLargeRight } from "../Arrows/icon-arrow-ui/icon-arrow-ui-large-right.svg";
+
+type BannerType = {
+ link: string;
+ title: string;
+ imageSrc?: ReactNode;
+ description?: string;
+};
+
+const Banner: FC = ({ link, imageSrc, title, description }) => {
+ const backgroundImageStyle = imageSrc
+ ? { backgroundImage: `url(${imageSrc})` }
+ : {};
+ return (
+
+
+
+ {title && (
+
) words.
+ // eslint-disable-next-line react/no-danger
+ dangerouslySetInnerHTML={{ __html: title }}
+ />
+ )}
+ {description &&
{description}
}
+
+
+
+
+ );
+};
+
+export default Banner;
diff --git a/src/stories/Library/banner/banner.scss b/src/stories/Library/banner/banner.scss
new file mode 100644
index 000000000..7a6bd4e47
--- /dev/null
+++ b/src/stories/Library/banner/banner.scss
@@ -0,0 +1,79 @@
+$_banner-content-wrapper-width: 944px;
+$_banner-content-max-width-small: 330px;
+$_banner-content-max-width-medium: 569px;
+$_banner-content-no-image-max-width-small: $layout__max-width--small;
+$_banner-height-small: 500px;
+$_banner-height-medium: 810px;
+
+.banner {
+ @include layout-container($layout__max-width--large, 0);
+
+ display: grid;
+ align-items: center;
+ text-decoration: none;
+ box-sizing: border-box;
+ background-color: $color__global-secondary;
+ min-height: $_banner-height-small;
+
+ &--has-image {
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: cover;
+ height: 100%;
+ width: 100%;
+ }
+
+ @include media-query__small {
+ min-height: $_banner-height-medium;
+ }
+}
+
+.banner__content-wrapper {
+ @include layout-container($_banner-content-wrapper-width, 0);
+ width: 100%;
+ padding: $s-2xl 0;
+
+ &--has-image {
+ padding-right: $s-2xl;
+ }
+
+ @include media-query__small {
+ padding-right: unset;
+ }
+}
+
+.banner__content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: $s-md;
+ text-align: center;
+ padding: $s-xl;
+ margin: 0 auto;
+ max-width: $_banner-content-max-width-medium;
+
+ &--has-image {
+ padding: $s-xl;
+ margin: unset;
+ text-align: unset;
+ align-items: unset;
+ background-color: $color__global-white;
+ max-width: $_banner-content-max-width-small;
+ }
+
+ @include media-query__small {
+ gap: $s-lg;
+ padding: calc($s-2xl + 6px);
+ max-width: $_banner-content-max-width-medium;
+ }
+}
+
+.banner__title {
+ @include typography($typo__h2);
+ @include u-extending-underline-style;
+}
+
+.banner__description {
+ @include typography($typo__body-placeholder);
+ line-height: 160%;
+}
diff --git a/src/styles/scss/tools/mixins.tools.scss b/src/styles/scss/tools/mixins.tools.scss
index a78af14f2..62a722ab6 100644
--- a/src/styles/scss/tools/mixins.tools.scss
+++ b/src/styles/scss/tools/mixins.tools.scss
@@ -70,3 +70,29 @@
text-overflow: ellipsis;
text-align: left;
}
+
+/**
+ * Mixin `u-extending-underline-style` applies a custom underline effect to `` elements.
+ * - Uses a pseudo-element (::after) with a positioned SVG image as the underline.
+ * - Removes default text underline.
+ */
+@mixin u-extending-underline-style {
+ u {
+ position: relative;
+ text-decoration: none;
+
+ &::after {
+ content: "";
+ position: absolute;
+ bottom: -5px;
+ left: 0;
+ width: 100%;
+ height: 12px;
+ background-repeat: no-repeat;
+ background-position: left;
+ background-size: cover;
+ // Underlined icon from public/icons/basic/icon-underlined.svg
+ background-image: url("data:image/svg+xml,%3Csvg width='205' height='8' viewBox='0 0 205 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.11133 6.87425C73.7223 0.129386 128.695 1.4981 203.889 1.8943' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
+ }
+ }
+}