diff --git a/packages/ui-library/.storybook/style.css b/packages/ui-library/.storybook/style.css
index c07e0cb543a..9e828172b90 100644
--- a/packages/ui-library/.storybook/style.css
+++ b/packages/ui-library/.storybook/style.css
@@ -30,6 +30,7 @@
@import "../src/components/modal/style.css";
@import "../src/components/notifications/style.css";
@import "../src/components/pagination/style.css";
+@import "../src/components/popover/style.css";
@import "../src/components/radio-group/style.css";
@import "../src/components/root/style.css";
@import "../src/components/select-field/style.css";
diff --git a/packages/ui-library/src/components/popover/docs/component.md b/packages/ui-library/src/components/popover/docs/component.md
new file mode 100644
index 00000000000..86c438f763d
--- /dev/null
+++ b/packages/ui-library/src/components/popover/docs/component.md
@@ -0,0 +1,4 @@
+A popover element is a type of modal interface used to display interactive content or additional information without navigating away from the current page.
+Unlike a tooltip, which typically appears on hover and provides brief, non-interactive information, a popover is usually triggered by clicking an element and can contain
+richer elements such as buttons, links, forms, or detailed explanations. The popover remains visible until the user explicitly dismisses it, offering a more
+persistent and interactive experience than a tooltip.
diff --git a/packages/ui-library/src/components/popover/docs/index.js b/packages/ui-library/src/components/popover/docs/index.js
new file mode 100644
index 00000000000..a01392a1542
--- /dev/null
+++ b/packages/ui-library/src/components/popover/docs/index.js
@@ -0,0 +1 @@
+export { default as component } from "./component.md";
diff --git a/packages/ui-library/src/components/popover/index.js b/packages/ui-library/src/components/popover/index.js
new file mode 100644
index 00000000000..f0a7b7c7783
--- /dev/null
+++ b/packages/ui-library/src/components/popover/index.js
@@ -0,0 +1,230 @@
+import classNames from "classnames";
+import PropTypes from "prop-types";
+import { XIcon } from "@heroicons/react/outline";
+import React, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, Fragment } from "react";
+import { Transition } from "@headlessui/react";
+import { noop } from "lodash";
+
+const PopoverContext = createContext( { handleDismiss: noop } );
+
+const positionClassNameMap = {
+ "no-arrow": "yst-popover--no-arrow",
+ top: "yst-popover--top",
+ "top-left": "yst-popover--top-left",
+ "top-right": "yst-popover--top-right",
+ right: "yst-popover--right",
+ bottom: "yst-popover--bottom",
+ left: "yst-popover--left",
+ "bottom-left": "yst-popover--bottom-left",
+ "bottom-right": "yst-popover--bottom-right",
+};
+
+/**
+ * @returns {Object} The popover context.
+ */
+export const usePopoverContext = () => useContext( PopoverContext );
+
+/**
+ * @param {string} dismissScreenReaderLabel The screen reader label for the dismiss button.
+ * @param {string} [className] The additional class name.
+ * @returns {JSX.Element} The close button.
+ */
+const CloseButton = ( {
+ dismissScreenReaderLabel,
+} ) => {
+ const { handleDismiss } = usePopoverContext();
+ const closeButtonRef = useRef( null );
+
+ return (
+
+
+
+ );
+};
+
+CloseButton.propTypes = {
+ dismissScreenReaderLabel: PropTypes.string.isRequired,
+};
+
+/**
+ * @param {string} title The popover title.
+ * @param {string} id The id of the title.
+ * @param {string} [className] The additional class name.
+ * @returns {JSX.Element} The title.
+ */
+const Title = ( {
+ title,
+ id,
+ className,
+} ) => {
+ return
+ { title }
+
;
+};
+
+Title.propTypes = {
+ title: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ className: PropTypes.string,
+};
+
+/**
+ * @param {string|string[]} content The popover content.
+ * @param {string } id The id of the content for accessibility.
+ * @param {string} [className] The additional class name.
+ * @returns {JSX.Element} The content.
+ */
+const Content = ( {
+ content,
+ id,
+ className,
+} ) => {
+ return (
+
+ { content }
+
+ );
+};
+
+Content.propTypes = {
+ content: PropTypes.oneOfType( [ PropTypes.node, PropTypes.arrayOf( PropTypes.node ) ] ),
+ id: PropTypes.string,
+ className: PropTypes.string,
+};
+
+/**
+ * @param {string} [className] The additional class name.
+ * @param {boolean} isVisible Whether the backdrop is visible.
+ * @returns {JSX.Element} The backdrop.
+ */
+const Backdrop = ( {
+ className, isVisible,
+} ) => {
+ useEffect( () => {
+ if ( isVisible ) {
+ document.body.classList.add( "backdrop-active" );
+ } else {
+ document.body.classList.remove( "backdrop-active" );
+ }
+ }, [ isVisible ] );
+ return (
+
+
+
+ );
+};
+
+Backdrop.propTypes = {
+ className: PropTypes.string,
+ isVisible: PropTypes.bool.isRequired,
+};
+
+/**
+ * @param {JSX.node} children Children of the popover.
+ * @param {string} id The popover id.
+ * @param {string} role The role of the popover.
+ * @param {string|JSX.Element} [as] Base component.
+ * @param {string} [className] Additional CSS classes.
+ * @param {string} [position] The position of the popover.
+ * @param {boolean} isVisible Whether the popover is visible.
+ * @param {Function} setIsVisible Function to set the visibility of the element.
+ * @param { JSX.Element } backdrop The backdrop of the popover.
+ * @returns {JSX.Element} The popover component.
+ */
+
+const Popover = forwardRef( ( {
+ children,
+ id,
+ role,
+ as: Component,
+ className,
+ isVisible,
+ setIsVisible,
+ position,
+ backdrop,
+ ...props
+}, ref ) => {
+ const handleDismiss = useCallback( () => {
+ setIsVisible( false );
+ }, [ setIsVisible ] );
+
+ return (
+
+ { backdrop && }
+
+
+ { children }
+
+
+
+ );
+} );
+
+Popover.displayName = "Popover";
+Popover.propTypes = {
+ as: PropTypes.elementType,
+ children: PropTypes.node.isRequired,
+ id: PropTypes.string.isRequired,
+ role: PropTypes.string,
+ className: PropTypes.string,
+ isVisible: PropTypes.bool,
+ setIsVisible: PropTypes.func,
+ position: PropTypes.oneOf( Object.keys( positionClassNameMap ) ),
+ backdrop: PropTypes.bool,
+};
+
+Popover.defaultProps = {
+ as: "div",
+ role: "dialog",
+ isVisible: false,
+ setIsVisible: false,
+ position: "no-arrow",
+ backdrop: false,
+ className: "",
+};
+
+Popover.Title = Title;
+Popover.CloseButton = CloseButton;
+Popover.Content = Content;
+Popover.Backdrop = Backdrop;
+
+export default Popover;
+
diff --git a/packages/ui-library/src/components/popover/stories.js b/packages/ui-library/src/components/popover/stories.js
new file mode 100644
index 00000000000..00bdd2d9a0f
--- /dev/null
+++ b/packages/ui-library/src/components/popover/stories.js
@@ -0,0 +1,160 @@
+import React, { useState } from "react";
+import Popover, { usePopoverContext } from "./index";
+import { component } from "./docs";
+import { InteractiveDocsPage } from "../../../.storybook/interactive-docs-page";
+import Button from "../../elements/button";
+import { noop } from "lodash";
+import { ValidationIcon } from "../../elements/validation";
+
+const DismissButton = () => {
+ const { handleDismiss } = usePopoverContext();
+ return ;
+};
+export const Factory = {
+ component: Popover,
+ render: ( args ) => {
+ return (
+ <>
+ Element
+
+ >
+
+ );
+ },
+ parameters: {
+ controls: { disable: true },
+ },
+ args: {
+ children: (
+
+ ),
+ },
+};
+
+export const WithMoreContent = {
+ render: ( args ) => {
+ const [ isVisible, setIsVisible ] = useState( true );
+
+ return (
+ <>
+ { isVisible && }
+ >
+ );
+ },
+ args: {
+ children: (
+ <>
+
+ >
+ ),
+ },
+ parameters: {
+ controls: { disable: true },
+ },
+};
+
+export const ButtonWithAPopover = {
+ render: ( args ) => {
+ const [ isVisible, setIsVisible ] = useState( false );
+
+ const handleClick = () => setIsVisible( ! isVisible );
+
+ return (
+
+ );
+ },
+ parameters: {
+ controls: { disable: false },
+ },
+ args: {
+ backdrop: true,
+ children: (
+ <>
+
+
+
+
+ >
+ ),
+ },
+};
+
+export default {
+ title: "2) Components/Popover",
+ component: Popover,
+ argTypes: {
+ children: { control: "text" },
+ },
+ args: {
+ id: "yst-popover",
+ isVisible: true,
+ setIsVisible: noop,
+ children: "",
+ backdrop: false,
+ },
+ parameters: {
+ docs: {
+ description: { component },
+ page: () => (
+
+ ),
+ },
+ },
+ decorators: [
+ ( Story ) => (
+
+ ),
+ ],
+};
diff --git a/packages/ui-library/src/components/popover/style.css b/packages/ui-library/src/components/popover/style.css
new file mode 100644
index 00000000000..1ef233091aa
--- /dev/null
+++ b/packages/ui-library/src/components/popover/style.css
@@ -0,0 +1,235 @@
+body[backdrop-active] * {/* Disable interaction with background elements when backdrop is active */
+ pointer-events: none;
+}
+
+@layer components {
+ .yst-root {
+
+ .yst-close-button-wrapper {
+ @apply
+ yst-flex-shrink-0
+ yst-flex
+ yst-self-start;
+
+ & button {
+ @apply
+ yst-bg-transparent
+ yst-rounded-md
+ yst-inline-flex
+ yst-text-slate-400
+ hover:yst-text-slate-500
+ focus:yst-outline-none
+ focus:yst-ring-2
+ focus:yst-ring-primary-500;
+ }
+ }
+
+ .yst-popover-title {
+ @apply
+ yst-text-sm
+ yst-font-medium
+ yst-text-slate-800
+ rtl:yst-text-right;
+ }
+
+ .yst-popover-backdrop {
+ @apply
+ yst-fixed
+ yst-inset-0
+ yst-bg-slate-500
+ yst-bg-opacity-75
+ yst-z-20
+ yst-pointer-events-auto;
+ }
+
+ .yst-popover {
+ @apply
+ yst-absolute
+ yst-whitespace-normal
+ yst-p-4
+ yst-rounded-lg
+ yst-w-max
+ yst-max-w-xs
+ sm:yst-max-w-sm
+ yst-z-30
+ yst-bg-white
+ yst-shadow-2xl
+ yst-gap-3
+ yst-border
+ before:yst-absolute;
+ }
+
+ .yst-popover--no-arrow {
+ @apply
+ yst--translate-x-1/2
+ yst-start-1/2
+ }
+
+ .yst-popover--right {
+ @apply
+ yst-translate-x-5
+ rtl:yst--translate-x-5
+ yst--translate-y-1/2
+ yst-top-1/2
+ yst-start-full
+ before:yst-content-['']
+ before:yst-block
+ before:yst-end-full
+ before:yst-top-1/2
+ before:yst-right-full
+ before:yst--translate-y-1/2
+ rtl:before:yst-translate-x-1/2;
+
+ &::before {
+ border-left: 14px solid transparent;
+ border-right: 14px solid #fff;
+ border-top: 14px solid transparent;
+ border-bottom: 14px solid transparent;
+ }
+ }
+
+ .yst-popover--top {
+ @apply
+ yst--translate-y-full
+ yst-translate-x-1/2
+ rtl:yst--translate-x-1/2
+ yst-end-1/2
+ yst--top-5
+ before:yst-content-['']
+ before:yst-start-1/2
+ before:yst-top-full
+ before:yst--translate-x-1/2
+ rtl:before:yst-translate-x-1/2
+ before:yst-translate-y-0;
+
+ &::before {
+ border-right: 14px solid transparent;
+ border-top: 14px solid #fff;
+ border-left: 14px solid transparent;
+ }
+ }
+
+ .yst-popover--top-left {
+ @apply
+ yst--translate-y-full
+ yst--top-5
+ yst-end-0
+ yst-translate-x-0
+ before:yst-content-['']
+ before:yst-end-2 /* move arrow to the right */
+ before:yst-top-full
+ before:yst--translate-x-1/2
+ rtl:before:yst-translate-x-1/2
+ before:yst-translate-y-0
+ before:yst-border-transparent;
+
+ &::before {
+ border-right: 14px solid transparent;
+ border-top: 14px solid #fff;
+ border-left: 14px solid transparent;
+ }
+ }
+
+ .yst-popover--top-right {
+ @apply
+ yst--translate-y-full
+ yst--top-5
+ yst--start-0
+ before:yst-content-['']
+ before:yst-start-8 /* move arrow to the left */
+ before:yst-top-full
+ before:yst--translate-x-1/2
+ rtl:before:yst-translate-x-1/2
+ before:yst-translate-y-0
+ before:yst-border-transparent;
+
+ &::before {
+ border-right: 14px solid transparent;
+ border-top: 14px solid #fff;
+ border-left: 14px solid transparent;
+ }
+ }
+
+ .yst-popover--left {
+ @apply
+ yst-top-1/2
+ yst-end-full
+ yst--start-5
+ yst--translate-x-full
+ yst--translate-y-1/2
+ rtl:yst-start-full
+ rtl:yst--end-5
+ rtl:yst-translate-x-full
+ before:yst-content-['']
+ before:yst-start-full
+ before:yst-top-1/2
+ before:yst-right-full
+ before:yst--translate-y-1/2
+ rtl:before:yst-translate-x-1/2;
+
+ &::before {
+ border-right: 14px solid transparent;
+ border-top: 14px solid transparent;
+ border-bottom: 14px solid transparent;
+ border-left: 14px solid #fff;
+ }
+ }
+
+ .yst-popover--bottom {
+ @apply
+ yst--translate-x-1/2
+ yst-start-1/2
+ yst-top-14
+ before:yst-content-['']
+ before:yst-start-1/2
+ before:yst-bottom-full
+ before:yst--translate-x-1/2
+ rtl:before:yst-translate-x-1/2
+ before:yst-translate-y-0;
+
+ &::before {
+ border-right: 14px solid transparent;
+ border-bottom: 14px solid #fff;
+ border-left: 14px solid transparent;
+ }
+ }
+
+ .yst-popover--bottom-left {
+ @apply
+ yst-end-0
+ yst-top-14
+ before:yst-content-['']
+ before:yst-end-0
+ before:yst-bottom-full
+ before:yst--translate-x-1/2
+ rtl:before:yst-translate-x-1/2
+ before:yst-translate-y-0
+ before:yst-border-transparent;
+
+ &::before {
+ border-right: 14px solid transparent;
+ border-bottom: 14px solid #fff;
+ border-left: 14px solid transparent;
+ }
+ }
+
+ .yst-popover--bottom-right {
+ @apply
+ yst-start-0
+ yst-top-14
+ before:yst-content-['']
+ before:yst-start-8
+ before:yst-bottom-full
+ before:yst--translate-x-1/2
+ rtl:before:yst-translate-x-1/2
+ before:yst-translate-y-0
+ before:yst-border-transparent;
+
+ &::before {
+ border-right: 14px solid transparent;
+ border-bottom: 14px solid #fff;
+ border-left: 14px solid transparent;
+ }
+ }
+ }
+}
diff --git a/packages/ui-library/src/index.js b/packages/ui-library/src/index.js
index 1c4c9c962a8..043452faa1e 100644
--- a/packages/ui-library/src/index.js
+++ b/packages/ui-library/src/index.js
@@ -32,6 +32,7 @@ export { default as FileImport } from "./components/file-import";
export { default as Modal } from "./components/modal";
export { default as Notifications, useNotificationsContext } from "./components/notifications";
export { default as Pagination } from "./components/pagination";
+export { default as Popover, usePopoverContext } from "./components/popover";
export { default as RadioGroup } from "./components/radio-group";
export { default as Root } from "./components/root";
export { default as SelectField } from "./components/select-field";