I’m starting to think this isn’t possible but i have seen multiple examples of it working without a scrollable element. I would like to have the tool tip element show beyond the bounds of a scrollable element.
This is my current layout for a chat application i am working on. If you hover over the emote img the tooltip will appear and you can see that when the emote is near the end of line that meets the right side border of its parents it will be hidden. It would be great to find a solution using only CSS.
If not I’m thinking i have to put the tool-tip outside of the element that holds messages and somehow make the tool-tip reusable for each emote. Does anyone have a good idea how to handle this better?
On a side note I’m also trying to fix the way i display imgs inside the tooltip. Currently it scales emotes to a minimum height of 128px. If a wide emote is inserted it will center the image horizontally and clip the left and right side. This doesn’t look terrible but I’m having trouble figuring out how to get the tooltip to have a dynamic width based on the img.
Any help is appreciated thanks!
:root {
--font-size: 14px;
--background-color: #18181b;
--background-transparent: #171717c4;
--accent-color: #0e0e10;
--accent-color-transparent: #0e0e1033;
--accent-color-alt: #18181b;
--border-color: #2a2a2e;
--text-color: #efeff1;
--text-color-alt: #adadb8;
--text-color-dark: #000000;
--primary-color: #9147ff;
--primary-color-hover: #772ce8;
--primary-color-transparent: #9147ff6b;
--first-msg-background: #c832c81A;
--first-msg-stroke: #2e0b2e;
--first-msg-border: #c832c8;
--success-color: #00f593;
--error-color: #eb0400;
--warning-color: #ffd37a;
--info-color: #1f69ff;
--px1: 0.0625rem;
--px2: 0.125rem;
--px3: 0.1875rem;
--px4: 0.25rem;
--px5: 0.3125rem;
--px6: 0.375rem;
--px7: 0.4375rem;
--px8: 0.5rem;
--px9: 0.5625rem;
--px12: 0.75rem;
--px14: 0.875rem;
--px16: 1rem;
--px20: 1.25rem;
--px24: 1.5rem;
--px28: 1.75rem;
--px32: 2rem;
--px36: 2.25rem;
--px40: 2.5rem;
--px48: 3rem;
--px56: 3.5rem;
--px64: 4rem;
--px72: 4.5rem;
--px80: 5rem;
--px96: 6rem;
--px100: 6.25rem;
--px112: 7rem;
--px128: 8rem;
--px144: 9rem;
--px160: 10rem;
--px192: 12rem;
--px256: 16rem;
--px290: 18.125rem;
--px304: 19rem;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
font-size: var(--font-size);
line-height: var(--px16);
color: var(--text-color);
}
body {
display: flex;
flex-direction: column;
font-family: "Inter", "Roobert", Helvetica, Arial, sans-serif;
background-color: var(--background-color);
color: var(--text-color);
overflow-x: hidden;
}
textarea,
input {
font-family: inherit;
border: var(--px3) solid var(--border-color);
background-color: var(--accent-color);
border-radius: var(--px6);
margin-bottom: var(--px12);
padding: var(--px6);
color: var(--text-color);
resize: none;
line-height: var(--px24);
}
textarea:focus,
input:focus {
outline: var(--primary-color) solid var(--px3);
border: var(--primary-color) solid var(--px3);
}
button {
font-family: inherit;
font-weight: 700;
border-radius: var(--px6);
padding: var(--px6) var(--px12);
background-color: var(--primary-color);
border: none;
color: var(--text-color);
margin-top: var(--px6);
cursor: pointer;
transition: all 0.2s ease-in-out;
}
button:hover {
background-color: var(--primary-color-hover);
}
#layout {
display: flex;
flex-direction: column;
height: 100%;
}
.panels {
display: grid;
grid-template-columns: auto 40dvw;
height: 100%;
}
.panel {
background-color: var(--accent-color);
padding: var(--px16);
}
.left-panel {
display: flex;
flex-direction: column;
background: var(--accent-color);
height: 100%;
overflow: hidden;
}
.right-panel {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
#chat::before {
content: "";
flex-grow: 1;
}
#chat {
display: flex;
flex-direction: column;
overflow: hidden;
height: 100%;
position: relative;
}
#send-chat {
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: space-between;
background: var(--accent-color);
padding: var(--px9);
flex: 0 0 var(--px96);
}
#chat-input {
height: var(--px40);
width: calc(100% - var(--px100));
padding: 0 var(--px48);
margin: 0;
}
#chat-button {
margin: 0;
}
#chat-button {
margin: 0;
}
#pause-chat {
display: none;
margin: 0;
font-size: var(--px14);
font-weight: normal;
color: var(--text-color);
position: absolute;
bottom: var(--px112);
left: 50%;
transform: translateX(-50%);
cursor: pointer;
background: var(--accent-color-transparent);
backdrop-filter: blur(var(--px6));
border: var(--px3) solid var(--border-color);
border-radius: var(--px6);
padding: var(--px6);
}
#messages {
width: 100%;
line-height: var(--px20);
scrollbar-width: none;
overflow-y: scroll;
overflow-x: hidden;
position: relative;
padding-top: var(--px192);
}
.message {
display: block;
position: relative;
overflow-wrap: anywhere;
}
.message::selection,
.message *::selection {
background-color: var(--primary-color-transparent) !important;
}
.message-background {
display: block;
position: relative;
padding: var(--px4) var(--px9);
}
.twitch-user {
font-weight: 700;
}
.twitch-message {
-webkit-text-stroke: var(--px3) var(--accent-color);
paint-order: stroke fill;
}
.message-tooltip {
display: flex;
flex-direction: column;
visibility: hidden;
opacity: 0;
position: absolute;
bottom: 112%;
width: var(--px128);
min-height: var(--px128);
line-height: var(--px12);
padding: var(--px12);
text-align: center;
font-size: var(--px12);
background-color: var(--accent-color-transparent);
border-radius: var(--px6);
box-shadow: 0 0 var(--px12) 0 rgba(255, 255, 255, 0.3);
backdrop-filter: blur(var(--px6));
transition: opacity 0.3s ease-in-out;
overflow: hidden;
word-wrap: break-word;
}
.message-tooltip-shift {
left: 50%;
transform: translateX(-50%);
}
.message-tooltip img {
display: block;
width: auto;
height: var(--px128);
max-width: none;
object-fit: cover;
}
.emote {
display: inline-grid;
margin: calc(var(--px4) * -1);
margin-left: 0 !important;
margin-right: 0 !important;
vertical-align: middle;
cursor: pointer;
/* position: relative; */
}
.emote:hover .message-tooltip {
visibility: visible;
opacity: 1;
}
.emote:hover {
transform: translateY(-6%);
}
.emote img {
border: none;
vertical-align: top;
max-width: 100%;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="layout">
<div class="panels">
<div class="left-panel">
<div id="chat">
<div id="messages">
<div id="e3483a7d-42f5-44d8-a1c3-30d0d97d7870" class="message">
<span class="message-background">
<span class="twitch-message">
<span class="twitch-user" style="color: #FF8C69;">NiceShyGuy</span><span class="message-text">: </span>
Lorem ipsum dolor sit amet, consect
<span class="emote">
<img src="https://cdn.betterttv.net/emote/567b00c61ddbe1786688a633/1x" class="bttv-emote">
<div class="message-tooltip message-tooltip-shift"><img src="https://cdn.betterttv.net/emote/567b00c61ddbe1786688a633/3x" alt="LuL">LuL</div>
</span>
</span>
</span>
</div>
<div id="e3483a7d-42f5-44d8-a1c3-30d0d97d7870" class="message">
<span class="message-background">
<span class="twitch-message">
<span class="twitch-user" style="color: #FF8C69;">NiceShyGuy</span><span class="message-text">: </span>
Lorem ipsum dolor sit amet, consect
<span class="emote">
<img src="https://cdn.7tv.app/emote/636c96c3cb5f97f7fb2f1ffa/1x.avif" class="seventv-emote">
<div class="message-tooltip message-tooltip-shift"><img src="https://cdn.7tv.app/emote/636c96c3cb5f97f7fb2f1ffa/4x.avif" alt="BocchiPossessed">BocchiPossessed</div>
</span>
</span>
</span>
</div>
</div>
<div id="send-chat">
<input id="chat-input" type="text" placeholder="Send a message">
<button id="chat-button">Chat</button>
</div>
<button id="pause-chat">⏸️ <span id="pause-text">Chat Paused</span></button>
</div>
</div>
<div class="right-panel"></div>
</div>
</div>
</body>
</html>
2
Answers
I ended up giving in and going with a js solution. I placed a tool tip element outside of the scrollable element and use js to rip/fix emote URLs and add names to the tooltip and reposition it above the emote on event mouseover.
Interesting question!
I would have calculated the tooltip
DIV
left
andright
positions based on its’width
property like this. I can see the width is given in--px128
CSS
variable.I did play around with resizing the window this requires a bit of fine tuning when icon reaches left and right margins of the parent.