diff --git a/components/system/components/fragments/Tooltip.js b/components/system/components/fragments/Tooltip.js
new file mode 100644
index 00000000..fe04a11d
--- /dev/null
+++ b/components/system/components/fragments/Tooltip.js
@@ -0,0 +1,204 @@
+import * as React from "react";
+
+import { css } from "@emotion/react";
+import { ModalPortal } from "~/components/core/ModalPortal";
+import { useEscapeKey, useEventListener } from "~/common/hooks";
+import { Boundary } from "~/components/system/components/fragments/Boundary";
+import { motion } from "framer-motion";
+
+/* -------------------------------------------------------------------------------------------------
+ * Root
+ * -----------------------------------------------------------------------------------------------*/
+
+const TooltipContext = React.createContext({});
+const useTooltipContext = () => React.useContext(TooltipContext);
+
+export function Root({ children, vertical = "below", horizontal = "right" }) {
+ const [state, setState] = React.useState({ isOpen: false, triggerRef: { current: null } });
+ const showTooltip = () => setState((prev) => ({ ...prev, isOpen: true }));
+ const hideTooltip = () => setState((prev) => ({ ...prev, isOpen: false }));
+ const setTriggerRef = (triggerRef) => setState((prev) => ({ ...prev, triggerRef }));
+
+ const contextValue = React.useMemo(
+ () => ({ showTooltip, hideTooltip, setTriggerRef, vertical, horizontal, ...state }),
+ [state, vertical, horizontal]
+ );
+
+ return {children};
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * Trigger
+ * -----------------------------------------------------------------------------------------------*/
+
+export function Trigger({ children, ...props }) {
+ const { setTriggerRef, showTooltip, hideTooltip } = useTooltipContext();
+
+ const ref = React.useRef();
+ React.useEffect(() => {
+ if (ref.current) setTriggerRef(ref);
+ }, []);
+
+ useEventListener({ type: "mouseenter", handler: showTooltip, ref });
+ useEventListener({ type: "mouseleave", handler: hideTooltip, ref });
+ useEventListener({ type: "click", handler: hideTooltip, ref });
+ useEventListener({ type: "focus", handler: showTooltip, ref });
+ useEventListener({ type: "blur", handler: hideTooltip, ref });
+
+ return React.cloneElement(React.Children.only(children), {
+ role: "tooltip",
+ ...props,
+ ref,
+ });
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * Content
+ * -----------------------------------------------------------------------------------------------*/
+const getTooltipPosition = (triggerElement, TooltipElement, vertical, horizontal) => {
+ let yOffset = window.pageYOffset;
+ let xOffset = window.pageXOffset;
+
+ const triggerRect = triggerElement.getBoundingClientRect();
+ const tooltipRect = TooltipElement.getBoundingClientRect();
+
+ let position = {};
+ switch (vertical) {
+ case "above":
+ position.top = `${triggerRect.top - tooltipRect.height + yOffset}px`;
+ break;
+ case "up":
+ position.top = `${triggerRect.bottom - tooltipRect.height + yOffset}px`;
+ break;
+ case "center":
+ position.top = `${
+ triggerRect.top + 0.5 * triggerRect.height - 0.5 * tooltipRect.height + yOffset
+ }px`;
+ break;
+ case "down":
+ position.top = `${triggerRect.top + yOffset}px`;
+ break;
+ case "below":
+ position.top = `${triggerRect.bottom + yOffset}px`;
+ break;
+ }
+ switch (horizontal) {
+ case "far-left":
+ position.left = `${triggerRect.left - tooltipRect.width + xOffset}px`;
+ break;
+ case "left":
+ position.left = `${triggerRect.right - tooltipRect.width + xOffset}px`;
+ break;
+ case "center":
+ position.left = `${
+ triggerRect.left + 0.5 * triggerRect.width - 0.5 * tooltipRect.width + xOffset
+ }px`;
+ break;
+ case "right":
+ position.left = `${triggerRect.left + xOffset}px`;
+ break;
+ case "far-right":
+ position.left = `${triggerRect.right + xOffset}px`;
+ break;
+ }
+ return position;
+};
+
+const adjustTooltipDirection = (triggerElement, TooltipElement, vertical, horizontal) => {
+ let yOffset = triggerElement ? triggerElement.scrollTop : window.pageYOffset;
+ let xOffset = triggerElement ? triggerElement.scrollLeft : window.pageXOffset;
+
+ const triggerRect = triggerElement.getBoundingClientRect();
+ const tooltipRect = TooltipElement.getBoundingClientRect();
+
+ if (!vertical) {
+ if (tooltipRect.height > triggerRect.top + yOffset) {
+ vertical = "below";
+ } else {
+ vertical = "above";
+ }
+ }
+ if (!horizontal) {
+ if (tooltipRect.width / 2 > triggerRect.left + triggerRect.width / 2 + xOffset) {
+ horizontal = "right";
+ } else if (tooltipRect.width / 2 > triggerRect.right + triggerRect.width / 2 + xOffset) {
+ horizontal = "left";
+ } else {
+ horizontal = "center";
+ }
+ }
+ return { vertical, horizontal };
+};
+
+const STYLES_CONTENT_WRAPPER = (theme) => css`
+ z-index: 2342342342;
+ position: absolute;
+ top: 0;
+ left: 0;
+ border-radius: 8px;
+ border: 1px solid ${theme.semantic.borderGrayLight4};
+ padding: 6px 8px;
+
+ box-shadow: ${theme.shadow.darkSmall};
+ background-color: ${theme.semantic.bgWhite};
+ @supports ((-webkit-backdrop-filter: blur(75px)) or (backdrop-filter: blur(75px))) {
+ -webkit-backdrop-filter: blur(75px);
+ backdrop-filter: blur(75px);
+ }
+`;
+
+function ContentWrapper({ children, css, style, ...props }) {
+ const { hideTooltip, triggerRef, horizontal, vertical } = useTooltipContext();
+ const [position, setPosition] = React.useState();
+
+ const ref = React.useRef();
+
+ React.useLayoutEffect(() => {
+ if (ref) {
+ const direction = adjustTooltipDirection(
+ triggerRef.current,
+ ref.current,
+ vertical,
+ horizontal
+ );
+ const position = getTooltipPosition(
+ triggerRef.current,
+ ref.current,
+ direction.vertical,
+ direction.horizontal
+ );
+ setPosition(position);
+ }
+ }, []);
+
+ useEscapeKey(hideTooltip);
+ useEventListener({ type: "scroll", handler: hideTooltip });
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+export function Content({ children, ...props }) {
+ const { isOpen } = useTooltipContext();
+
+ if (!isOpen) return null;
+
+ return (
+
+ {children}
+
+ );
+}