diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png
new file mode 100644
index 0000000000..5f545757c9
Binary files /dev/null and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png differ
diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png
new file mode 100644
index 0000000000..d34c9969d5
Binary files /dev/null and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png differ
diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png
new file mode 100644
index 0000000000..ce0bab429c
Binary files /dev/null and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png differ
diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png
new file mode 100644
index 0000000000..4027a12565
Binary files /dev/null and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png differ
diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png
new file mode 100644
index 0000000000..54a3485909
Binary files /dev/null and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png differ
diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png
new file mode 100644
index 0000000000..405ceb6e55
Binary files /dev/null and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png differ
diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png
new file mode 100644
index 0000000000..e9291e761e
Binary files /dev/null and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png differ
diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png
new file mode 100644
index 0000000000..4e786a2296
Binary files /dev/null and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png differ
diff --git a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.text.BpkTextTest_hero2.png b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.text.BpkTextTest_hero2.png
index fe805b0916..dc63da4ae6 100644
Binary files a/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.text.BpkTextTest_hero2.png and b/app/screenshots/oss/debug/default/net.skyscanner.backpack.compose.text.BpkTextTest_hero2.png differ
diff --git a/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png
new file mode 100644
index 0000000000..49ceb7c073
Binary files /dev/null and b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png differ
diff --git a/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png
new file mode 100644
index 0000000000..e04e9e6a6c
Binary files /dev/null and b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png differ
diff --git a/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png
new file mode 100644
index 0000000000..4372f3c714
Binary files /dev/null and b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png differ
diff --git a/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png
new file mode 100644
index 0000000000..96b4e9afd8
Binary files /dev/null and b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png differ
diff --git a/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png
new file mode 100644
index 0000000000..1a118b1066
Binary files /dev/null and b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png differ
diff --git a/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png
new file mode 100644
index 0000000000..b772335887
Binary files /dev/null and b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png differ
diff --git a/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png
new file mode 100644
index 0000000000..aa835eb9b3
Binary files /dev/null and b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png differ
diff --git a/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png
new file mode 100644
index 0000000000..258954ca55
Binary files /dev/null and b/app/screenshots/oss/debug/dm/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png differ
diff --git a/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png
new file mode 100644
index 0000000000..26c89e814d
Binary files /dev/null and b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerAvailable.png differ
diff --git a/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png
new file mode 100644
index 0000000000..819efdbbf0
Binary files /dev/null and b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineAvailable.png differ
diff --git a/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png
new file mode 100644
index 0000000000..f145df7e00
Binary files /dev/null and b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_bottomAlignedKickerSubHeadlineSponsoredAvailable.png differ
diff --git a/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png
new file mode 100644
index 0000000000..3e19e191ba
Binary files /dev/null and b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_default.png differ
diff --git a/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png
new file mode 100644
index 0000000000..57f9f1a223
Binary files /dev/null and b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_defaultWithOverlay.png differ
diff --git a/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png
new file mode 100644
index 0000000000..82b5388e84
Binary files /dev/null and b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerAvailable.png differ
diff --git a/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png
new file mode 100644
index 0000000000..9023258256
Binary files /dev/null and b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineAvailable.png differ
diff --git a/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png
new file mode 100644
index 0000000000..b69fa78234
Binary files /dev/null and b/app/screenshots/oss/debug/rtl/net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoTest_topAlignedKickerSubHeadlineSponsoredAvailable.png differ
diff --git a/app/src/androidTest/java/net/skyscanner/backpack/compose/graphicpromotion/BpkGraphicPromoTest.kt b/app/src/androidTest/java/net/skyscanner/backpack/compose/graphicpromotion/BpkGraphicPromoTest.kt
new file mode 100644
index 0000000000..688d3c0b2f
--- /dev/null
+++ b/app/src/androidTest/java/net/skyscanner/backpack/compose/graphicpromotion/BpkGraphicPromoTest.kt
@@ -0,0 +1,80 @@
+/**
+ * Backpack for Android - Skyscanner's Design System
+ *
+ * Copyright 2018 Skyscanner Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.skyscanner.backpack.compose.graphicpromotion
+
+import net.skyscanner.backpack.compose.BpkSnapshotTest
+import net.skyscanner.backpack.compose.overlay.BpkOverlayType
+import net.skyscanner.backpack.demo.compose.BpkGraphicPromoSample
+import net.skyscanner.backpack.demo.compose.GraphicPromoStoryAlignmentBottomSponsored
+import net.skyscanner.backpack.demo.compose.GraphicPromoStoryAlignmentBottomWithText
+import net.skyscanner.backpack.demo.compose.GraphicPromoStoryAlignmentTopSponsored
+import net.skyscanner.backpack.demo.compose.GraphicPromoStoryAlignmentTopWithKicker
+import net.skyscanner.backpack.demo.compose.GraphicPromoStoryDefault
+import org.junit.Test
+
+class BpkGraphicPromoTest : BpkSnapshotTest() {
+
+ @Test
+ fun default() = snap {
+ GraphicPromoStoryDefault()
+ }
+
+ @Test
+ fun defaultWithOverlay() = snap {
+ BpkGraphicPromoSample(
+ headline = "Three Parks Challenge",
+ overlayType = BpkOverlayType.SolidHigh,
+ )
+ }
+
+ @Test
+ fun topAlignedKickerAvailable() = snap {
+ BpkGraphicPromoSample(
+ subHeadline = "How to complete the climb in 3 days",
+ )
+ }
+
+ @Test
+ fun topAlignedKickerSubHeadlineAvailable() = snap {
+ GraphicPromoStoryAlignmentTopWithKicker()
+ }
+
+ @Test
+ fun topAlignedKickerSubHeadlineSponsoredAvailable() = snap {
+ GraphicPromoStoryAlignmentTopSponsored()
+ }
+
+ @Test
+ fun bottomAlignedKickerAvailable() = snap {
+ BpkGraphicPromoSample(
+ subHeadline = "How to complete the climb in 3 days",
+ verticalAlignment = BpkGraphicPromoVerticalAlignment.Bottom,
+ )
+ }
+
+ @Test
+ fun bottomAlignedKickerSubHeadlineAvailable() = snap {
+ GraphicPromoStoryAlignmentBottomWithText()
+ }
+
+ @Test
+ fun bottomAlignedKickerSubHeadlineSponsoredAvailable() = snap {
+ GraphicPromoStoryAlignmentBottomSponsored()
+ }
+}
diff --git a/app/src/main/java/net/skyscanner/backpack/demo/components/GraphicPromoComponent.kt b/app/src/main/java/net/skyscanner/backpack/demo/components/GraphicPromoComponent.kt
new file mode 100644
index 0000000000..6cec8da5e0
--- /dev/null
+++ b/app/src/main/java/net/skyscanner/backpack/demo/components/GraphicPromoComponent.kt
@@ -0,0 +1,24 @@
+/**
+ * Backpack for Android - Skyscanner's Design System
+ *
+ * Copyright 2018 Skyscanner Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.skyscanner.backpack.demo.components
+
+import net.skyscanner.backpack.meta.ComponentMarker
+
+@ComponentMarker("GraphicPromo")
+annotation class GraphicPromoComponent
diff --git a/app/src/main/java/net/skyscanner/backpack/demo/compose/GraphicPromoStory.kt b/app/src/main/java/net/skyscanner/backpack/demo/compose/GraphicPromoStory.kt
new file mode 100644
index 0000000000..06c7dfab68
--- /dev/null
+++ b/app/src/main/java/net/skyscanner/backpack/demo/compose/GraphicPromoStory.kt
@@ -0,0 +1,144 @@
+/**
+ * Backpack for Android - Skyscanner's Design System
+ *
+ * Copyright 2018 Skyscanner Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.skyscanner.backpack.demo.compose
+
+import android.util.Log
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromo
+import net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicsPromoSponsor
+import net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoVerticalAlignment
+import net.skyscanner.backpack.compose.overlay.BpkOverlayType
+import net.skyscanner.backpack.compose.tokens.BpkSpacing
+import net.skyscanner.backpack.demo.R
+import net.skyscanner.backpack.demo.components.GraphicPromoComponent
+import net.skyscanner.backpack.demo.meta.ComposeStory
+
+@Composable
+@GraphicPromoComponent
+@ComposeStory
+internal fun GraphicPromoStoryDefault(modifier: Modifier = Modifier) {
+ Column(modifier) {
+ BpkGraphicPromoSample()
+ }
+}
+
+@Composable
+@GraphicPromoComponent
+@ComposeStory("Top align with text")
+internal fun GraphicPromoStoryAlignmentTopWithKicker() {
+ BpkGraphicPromoSample(
+ kicker = "Travel tips",
+ subHeadline = "How to complete the climb in 3 days",
+ )
+}
+
+@Composable
+@GraphicPromoComponent
+@ComposeStory("Bottom align with text")
+internal fun GraphicPromoStoryAlignmentBottomWithText() {
+ BpkGraphicPromoSample(
+ kicker = "Travel tips",
+ subHeadline = "How to complete the climb in 3 days",
+ verticalAlignment = BpkGraphicPromoVerticalAlignment.Bottom,
+ )
+}
+
+@Composable
+@GraphicPromoComponent
+@ComposeStory("Top align Sponsored")
+internal fun GraphicPromoStoryAlignmentTopSponsored() {
+ BpkGraphicPromoSample(
+ kicker = "Travel tips",
+ subHeadline = "How to complete the climb in 3 days",
+ sponsor = BpkGraphicsPromoSponsor(
+ accessibilityLabel = "Sponsored",
+ logo = "https://images.kiwi.com/airlines/64/FR.png",
+ title = "Sponsored",
+ ),
+ )
+}
+
+@Composable
+@GraphicPromoComponent
+@ComposeStory("Bottom align Sponsored")
+internal fun GraphicPromoStoryAlignmentBottomSponsored() {
+ BpkGraphicPromoSample(
+ kicker = "Travel tips",
+ subHeadline = "How to complete the climb in 3 days",
+ verticalAlignment = BpkGraphicPromoVerticalAlignment.Bottom,
+ sponsor = BpkGraphicsPromoSponsor(
+ accessibilityLabel = "Sponsored",
+ logo = "https://images.kiwi.com/airlines/64/FR.png",
+ title = "Sponsored",
+ ),
+ )
+}
+
+@Composable
+internal fun BpkGraphicPromoSample(
+ modifier: Modifier = Modifier,
+ headline: String = "Three Parks Challenge",
+ subHeadline: String? = null,
+ kicker: String? = null,
+ verticalAlignment: BpkGraphicPromoVerticalAlignment = BpkGraphicPromoVerticalAlignment.Top,
+ overlayType: BpkOverlayType? = null,
+ sponsor: BpkGraphicsPromoSponsor? = null,
+) {
+ BpkGraphicPromo(
+ modifier = modifier
+ .fillMaxSize()
+ .padding(BpkSpacing.Base),
+ kicker = kicker,
+ headline = headline,
+ subHeadline = subHeadline,
+ verticalAlignment = verticalAlignment,
+ overlayType = overlayType,
+ sponsor = sponsor,
+ image = {
+ Image(
+ modifier = Modifier.matchParentSize(),
+ painter = painterResource(
+ id = R.drawable.graphic_promo,
+ ),
+ contentDescription = "Image",
+ contentScale = ContentScale.Crop,
+ )
+ }, sponsorLogo = {
+ if (sponsor != null) {
+ Image(
+ painter = painterResource(
+ id = R.drawable.skyland,
+ ),
+ contentDescription = "Image",
+ contentScale = ContentScale.Fit,
+ )
+ }
+ },
+ tapAction = {
+ Log.d("BpkGraphicPromo", "Tap on graphic promo")
+ },
+ )
+}
diff --git a/app/src/main/res/drawable-nodpi/graphic_promo.jpg b/app/src/main/res/drawable-nodpi/graphic_promo.jpg
new file mode 100644
index 0000000000..3c30bc6e28
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/graphic_promo.jpg differ
diff --git a/app/src/main/res/drawable/skyairlines.xml b/app/src/main/res/drawable/skyairlines.xml
new file mode 100644
index 0000000000..5638b338ec
--- /dev/null
+++ b/app/src/main/res/drawable/skyairlines.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/skyland.xml b/app/src/main/res/drawable/skyland.xml
new file mode 100644
index 0000000000..d2010f219f
--- /dev/null
+++ b/app/src/main/res/drawable/skyland.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/graphicpromotion/BpkGraphicPromo.kt b/backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/graphicpromotion/BpkGraphicPromo.kt
new file mode 100644
index 0000000000..a4a9060fbe
--- /dev/null
+++ b/backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/graphicpromotion/BpkGraphicPromo.kt
@@ -0,0 +1,63 @@
+/**
+ * Backpack for Android - Skyscanner's Design System
+ *
+ * Copyright 2018 Skyscanner Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.skyscanner.backpack.compose.graphicpromotion
+
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import net.skyscanner.backpack.compose.graphicpromotion.internal.BpkGraphicPromoImpl
+import net.skyscanner.backpack.compose.overlay.BpkOverlayType
+
+enum class BpkGraphicPromoVariant { OnDark, OnLight, }
+enum class BpkGraphicPromoVerticalAlignment { Top, Bottom, }
+
+data class BpkGraphicsPromoSponsor(
+ val title: String,
+ val logo: String,
+ val accessibilityLabel: String,
+)
+
+@Composable
+fun BpkGraphicPromo(
+ headline: String,
+ image: @Composable BoxScope.() -> Unit,
+ modifier: Modifier = Modifier,
+ kicker: String? = null,
+ subHeadline: String? = null,
+ overlayType: BpkOverlayType? = null,
+ variant: BpkGraphicPromoVariant = BpkGraphicPromoVariant.OnDark,
+ verticalAlignment: BpkGraphicPromoVerticalAlignment = BpkGraphicPromoVerticalAlignment.Top,
+ sponsor: BpkGraphicsPromoSponsor? = null,
+ sponsorLogo: (@Composable () -> Unit)? = null,
+ tapAction: () -> Unit = {},
+) {
+ BpkGraphicPromoImpl(
+ headline = headline,
+ modifier = modifier,
+ kicker = kicker,
+ subHeadline = subHeadline,
+ overlayType = overlayType,
+ variant = variant,
+ verticalAlignment = verticalAlignment,
+ sponsor = sponsor,
+ image = image,
+ sponsorLogo = sponsorLogo,
+ tapAction = tapAction,
+ )
+}
diff --git a/backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/graphicpromotion/internal/BpkGraphicPromoImpl.kt b/backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/graphicpromotion/internal/BpkGraphicPromoImpl.kt
new file mode 100644
index 0000000000..ee4ec72414
--- /dev/null
+++ b/backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/graphicpromotion/internal/BpkGraphicPromoImpl.kt
@@ -0,0 +1,287 @@
+/**
+ * Backpack for Android - Skyscanner's Design System
+ *
+ * Copyright 2018 Skyscanner Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.skyscanner.backpack.compose.graphicpromotion.internal
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.Indication
+import androidx.compose.foundation.IndicationInstance
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.indication
+import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.graphics.drawscope.scale
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicsPromoSponsor
+import net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoVariant
+import net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromoVerticalAlignment
+import net.skyscanner.backpack.compose.overlay.BpkOverlay
+import net.skyscanner.backpack.compose.overlay.BpkOverlayType
+import net.skyscanner.backpack.compose.text.BpkText
+import net.skyscanner.backpack.compose.theme.BpkTheme
+import net.skyscanner.backpack.compose.tokens.BpkBorderRadius
+import net.skyscanner.backpack.compose.tokens.BpkSpacing
+
+@Composable
+internal fun BpkGraphicPromoImpl(
+ headline: String,
+ image: @Composable BoxScope.() -> Unit,
+ modifier: Modifier = Modifier,
+ kicker: String? = null,
+ subHeadline: String? = null,
+ overlayType: BpkOverlayType? = null,
+ variant: BpkGraphicPromoVariant = BpkGraphicPromoVariant.OnDark,
+ verticalAlignment: BpkGraphicPromoVerticalAlignment = BpkGraphicPromoVerticalAlignment.Top,
+ sponsor: BpkGraphicsPromoSponsor? = null,
+ sponsorLogo: (@Composable () -> Unit)? = null,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+ tapAction: () -> Unit = {},
+) {
+ val roundedCornerShape = RoundedCornerShape(BpkBorderRadius.Md)
+ val contentDescription = listOfNotNull(kicker, headline, subHeadline, sponsor?.accessibilityLabel)
+ .joinToString(separator = ", ")
+ Box(
+ modifier = modifier
+ .aspectRatio(RATIO_PORTRAIT)
+ .clip(roundedCornerShape)
+ .clickable(
+ interactionSource = interactionSource,
+ indication = null,
+ onClick = tapAction,
+ )
+ .semantics(mergeDescendants = true) {
+ role = Role.Button
+ this.contentDescription = contentDescription
+ },
+ ) {
+ if (overlayType != null) {
+ BpkOverlay(
+ modifier = Modifier
+ .matchParentSize()
+ .indication(interactionSource, InteractiveBackgroundIndication),
+ overlayType = overlayType,
+ foregroundContent = {
+ ForegroundContent(
+ headline = headline,
+ kicker = kicker,
+ subHeadline = subHeadline,
+ variant = variant,
+ verticalAlignment = verticalAlignment,
+ sponsor = sponsor,
+ sponsorLogo = sponsorLogo,
+ )
+ },
+ content = image,
+ )
+ } else {
+ Box(
+ modifier = Modifier
+ .matchParentSize()
+ .indication(interactionSource, InteractiveBackgroundIndication),
+ content = image,
+ )
+ ForegroundContent(
+ headline = headline,
+ kicker = kicker,
+ subHeadline = subHeadline,
+ variant = variant,
+ verticalAlignment = verticalAlignment,
+ sponsor = sponsor,
+ sponsorLogo = sponsorLogo,
+ )
+ }
+ }
+}
+
+@Composable
+private fun SponsorOverlayView(
+ textColor: Color,
+ sponsor: BpkGraphicsPromoSponsor?,
+ modifier: Modifier = Modifier,
+ sponsorLogo: (@Composable () -> Unit)? = null,
+) {
+ if (sponsor != null) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(BpkSpacing.Md, Alignment.Top),
+ ) {
+ BpkText(
+ text = sponsor.title,
+ style = BpkTheme.typography.label1,
+ color = textColor,
+ )
+
+ Box(
+ modifier = Modifier
+ .heightIn(max = SPONSOR_LOGO_HEIGHT.dp),
+ content = { sponsorLogo?.let { it() } },
+ )
+ }
+ }
+}
+
+@Composable
+private fun MessageOverlay(
+ headline: String,
+ kicker: String?,
+ subHeadline: String?,
+ textColor: Color,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(BpkSpacing.Md, Alignment.Top),
+ ) {
+ if (!kicker.isNullOrBlank()) {
+ BpkText(
+ text = kicker,
+ style = BpkTheme.typography.subheading,
+ color = textColor,
+ )
+ }
+
+ BpkText(
+ text = headline,
+ style = BpkTheme.typography.heading2,
+ color = textColor,
+ )
+ if (!subHeadline.isNullOrBlank()) {
+ BpkText(
+ text = subHeadline,
+ style = BpkTheme.typography.heading5,
+ color = textColor,
+ )
+ }
+ }
+}
+
+@Composable
+private fun ForegroundContent(
+ headline: String,
+ modifier: Modifier = Modifier,
+ kicker: String? = null,
+ subHeadline: String? = null,
+ variant: BpkGraphicPromoVariant = BpkGraphicPromoVariant.OnDark,
+ verticalAlignment: BpkGraphicPromoVerticalAlignment = BpkGraphicPromoVerticalAlignment.Top,
+ sponsor: BpkGraphicsPromoSponsor? = null,
+ sponsorLogo: (@Composable () -> Unit)? = null,
+) {
+ val textColor: Color = when (variant) {
+ BpkGraphicPromoVariant.OnDark -> BpkTheme.colors.textOnDark
+ BpkGraphicPromoVariant.OnLight -> BpkTheme.colors.textOnLight
+ }
+
+ Column(modifier = modifier.padding(BpkSpacing.Lg)) {
+ when (verticalAlignment) {
+ BpkGraphicPromoVerticalAlignment.Top -> {
+ MessageOverlay(
+ headline = headline,
+ kicker = kicker,
+ subHeadline = subHeadline,
+ textColor = textColor,
+ )
+
+ Spacer(Modifier.weight(1f))
+
+ SponsorOverlayView(
+ sponsor = sponsor,
+ textColor = textColor,
+ sponsorLogo = sponsorLogo,
+ )
+ }
+ BpkGraphicPromoVerticalAlignment.Bottom -> {
+ SponsorOverlayView(
+ sponsor = sponsor,
+ textColor = textColor,
+ sponsorLogo = sponsorLogo,
+ )
+
+ Spacer(Modifier.weight(1f))
+
+ MessageOverlay(
+ headline = headline,
+ kicker = kicker,
+ subHeadline = subHeadline,
+ textColor = textColor,
+ )
+ }
+ }
+ }
+}
+
+private object InteractiveBackgroundIndication : Indication {
+
+ @Composable
+ override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
+ val isPressed by interactionSource.collectIsPressedAsState()
+
+ val scale by animateFloatAsState(
+ targetValue = if (isPressed) 1.05f else 1.0f,
+ animationSpec = interactiveBackgroundAnimationSpec,
+ label = "background scale",
+ )
+
+ val overlayAlpha by animateFloatAsState(
+ targetValue = if (isPressed) 0.2f else 0f,
+ animationSpec = interactiveBackgroundAnimationSpec,
+ label = "overlay alpha",
+ )
+
+ return remember(interactionSource) {
+ object : IndicationInstance {
+ override fun ContentDrawScope.drawIndication() {
+ scale(scale, scale) {
+ this@drawIndication.drawContent()
+ }
+ drawRect(Color.Black, alpha = overlayAlpha)
+ }
+ }
+ }
+ }
+}
+
+private const val RATIO_PORTRAIT: Float = 3 / 4f
+private const val SPONSOR_LOGO_HEIGHT = 60
+
+private val interactiveBackgroundAnimationSpec: AnimationSpec = spring(
+ stiffness = 800f,
+ dampingRatio = 1f,
+)
diff --git a/docs/compose/GraphicPromo/README.md b/docs/compose/GraphicPromo/README.md
new file mode 100644
index 0000000000..5db03393e4
--- /dev/null
+++ b/docs/compose/GraphicPromo/README.md
@@ -0,0 +1,85 @@
+# GraphicPromo
+
+[![Maven Central](https://img.shields.io/maven-central/v/net.skyscanner.backpack/backpack-compose)](https://search.maven.org/artifact/net.skyscanner.backpack/backpack-compose)
+[![Class reference](https://img.shields.io/badge/Class%20reference-Android-blue)](https://backpack.github.io/android/backpack-compose/net.skyscanner.backpack.compose.graphicpromotion)
+[![Source code](https://img.shields.io/badge/Source%20code-GitHub-lightgrey)](https://github.com/Skyscanner/backpack-android/tree/main/backpack-compose/src/main/kotlin/net/skyscanner/backpack/compose/graphicpromotion)
+
+## Default
+
+| Day | Night |
+| --- | --- |
+| | |
+
+## Top aligned with kicker and sub headline
+
+| Day | Night |
+| --- | --- |
+| | |
+
+
+## Top aligned Sponsored
+
+| Day | Night |
+| --- | --- |
+| | |
+
+## Bottom aligned with kicker and sub headline
+
+| Day | Night |
+| --- | --- |
+| | |
+
+
+## Bottom aligned Sponsored
+
+| Day | Night |
+| --- | --- |
+| | |
+
+
+## Installation
+
+Backpack Compose is available through [Maven Central](https://search.maven.org/artifact/net.skyscanner.backpack/backpack-compose). Check the main [Readme](https://github.com/skyscanner/backpack-android#installation) for a complete installation guide.
+
+## Usage
+
+Example of a GraphicPromo:
+
+```Kotlin
+import android.util.Log
+import net.skyscanner.backpack.compose.graphicpromotion.BpkGraphicPromo
+import net.skyscanner.backpack.compose.graphicpromotion.Sponsor
+import net.skyscanner.backpack.compose.graphicpromotion.VerticalAlignment
+import net.skyscanner.backpack.compose.overlay.BpkOverlayType
+
+
+BpkGraphicPromo(
+ kicker = "Travel tips",
+ headline = "Three Parks Challenge",
+ subHeadline = "How to complete the climb in 3 days",
+ verticalAlignment = VerticalAlignment.Bottom,
+ overlayType = BpkOverlayType.SolidHigh,
+ sponsor = BpkGraphicsPromoSponsor(
+ accessibilityLabel = "Sponsored",
+ logo = "https://images.kiwi.com/airlines/64/FR.png",
+ title = "Sponsored",
+ ),
+ image = {
+ Image(
+ modifier = Modifier.matchParentSize(),
+ painter = painterResource(id = R.drawable.graphic_promo),
+ contentDescription = "Image",
+ contentScale = ContentScale.Crop,
+ )
+ }, sponsorLogo = {
+ Image(
+ painter = painterResource(id = R.drawable.skyland,),
+ contentDescription = "Image",
+ contentScale = ContentScale.Fit,
+ )
+ },
+ tapAction = {
+ Log.d("TAG", "Tap on graphic promo")
+ },
+)
+```
diff --git a/docs/compose/GraphicPromo/screenshots/bottom-align-sponsored.png b/docs/compose/GraphicPromo/screenshots/bottom-align-sponsored.png
new file mode 100644
index 0000000000..28ecf0d391
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/bottom-align-sponsored.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/bottom-align-sponsored_dm.png b/docs/compose/GraphicPromo/screenshots/bottom-align-sponsored_dm.png
new file mode 100644
index 0000000000..d2c65c9f34
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/bottom-align-sponsored_dm.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/bottom-align-with-text.png b/docs/compose/GraphicPromo/screenshots/bottom-align-with-text.png
new file mode 100644
index 0000000000..2e10245fed
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/bottom-align-with-text.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/bottom-align-with-text_dm.png b/docs/compose/GraphicPromo/screenshots/bottom-align-with-text_dm.png
new file mode 100644
index 0000000000..d152af482f
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/bottom-align-with-text_dm.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/default.png b/docs/compose/GraphicPromo/screenshots/default.png
new file mode 100644
index 0000000000..0f2487494e
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/default.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/default_dm.png b/docs/compose/GraphicPromo/screenshots/default_dm.png
new file mode 100644
index 0000000000..d13f0655d8
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/default_dm.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/top-align-sponsored.png b/docs/compose/GraphicPromo/screenshots/top-align-sponsored.png
new file mode 100644
index 0000000000..f8d1624240
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/top-align-sponsored.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/top-align-sponsored_dm.png b/docs/compose/GraphicPromo/screenshots/top-align-sponsored_dm.png
new file mode 100644
index 0000000000..5404237b7a
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/top-align-sponsored_dm.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/top-align-with-text.png b/docs/compose/GraphicPromo/screenshots/top-align-with-text.png
new file mode 100644
index 0000000000..0835fec72e
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/top-align-with-text.png differ
diff --git a/docs/compose/GraphicPromo/screenshots/top-align-with-text_dm.png b/docs/compose/GraphicPromo/screenshots/top-align-with-text_dm.png
new file mode 100644
index 0000000000..6d69153b8d
Binary files /dev/null and b/docs/compose/GraphicPromo/screenshots/top-align-with-text_dm.png differ