Absolute position element centered as long as it fits on the screen - css

I am creating a tooltip system using only CSS that displays tooltips absolute centered below the element you can hover over to see the tooltip. Tooltips must have a minimum width. In my example below, you will see this works fine on the second element. On the first though, the tooltip does not fit on the screen and will be cut off. In this scenario (the tooltip not fitting on screen on the left side) it should just left align. If it were on the right side of the screen, it should align right. I might be pushing the CSS boundaries, but might there be a CSS solution?
Edit: as others have pointed out, this cannot be done purely in CSS. JavaScript can be used with Element.getBoundingClientRect(). Of course, there are also pre-made solutions such as PopperJS.
[data-tooltip] {
padding: 3px 6px;
color: white;
background: red;
position: relative;
margin: 3px;
}
[data-tooltip]::after {
content: attr(data-tooltip);
position: absolute;
left: 50%;
transform: translateX(-50%);
margin: 2em auto;
opacity: 0;
pointer-events: none;
transition: all 0.4s ease-in-out;
padding: 10px;
color: #fff;
background: darkgray;
min-width: 100px;
box-shadow: 2px 2px 1px silver;
}
[data-tooltip]:hover::after {
opacity: 1;
}
<div style="padding: 10px 0px">
<span data-tooltip="This text (or element) will be cut off somewhat on the left side">Hover me
</span>
<span data-tooltip="This tooltip will be completely shown because it is not near any edge">Second one
</span>
</div>

Related

How to add a scroll to the buttons container?

I have this buttons which have a hover effect that renders a span on top of the hovered button.
It works fine, but, when the width of the screen changes, it looks very bad:
These are the elements:
<div className='buttons'>
<button
className={editor.isActive('bold') ? 'is-active' : 'is-inactive'}
>
<strong>N</strong>
<span className='popup'>Negrita (Ctrl+B)</span>
</button>
<button
className={editor.isActive('italic') ? 'is-active' : 'is-inactive'}
>
<em>C</em>
<span className='popup'>Cursiva (Ctrl+I)</span>
</button>
<button
className={editor.isActive('strike') ? 'is-active' : 'is-inactive'}
>
<s>T</s>
<span className='popup'>Tachado (Ctrl+Shift+X)</span>
</button>
... (you get the idea)
</div>
This is the scss:
.buttons button {
position: relative;
height: 29.19px;
color: #000;
border: 1px solid black;
border-radius: 0.3rem;
margin: 0.2rem !important;
padding: 0.1rem 0.4rem !important;
background: white;
accent-color: black;
font-weight: 500;
}
button .popup {
visibility: hidden;
width: 120px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
bottom: 100%;
left: 50%;
margin-bottom: 5px;
margin-left: -60px;
}
button .popup::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: black transparent transparent transparent;
}
button:hover .popup {
visibility: visible;
}
What im looking for
I decided that the best solution would be a horizontal scroll. The problem is that the span is absolute positioned based on the button so i cant figure it out a way to keep the span on top of the hovered button while having an horizontal scroll.
Update 1
Ok, i tried to add a scroll, this is whats happening now:
As you can see, the scroll is working fine but the spans are 'covered', is there a way to fix this?
I added this to the code:
.buttons {
position: relative;
overflow-x: scroll;
white-space: nowrap;
}
Have a look at the definition of the absolute here
The element is removed from the normal document flow, and no space is
created for the element in the page layout. It is positioned relative
to its closest positioned ancestor, if any; otherwise, it is placed
relative to the initial containing block.
And positioned ancestor:
A positioned element is an element whose computed position value is
either relative, absolute, fixed, or sticky. (In other words, it's
anything except static.)
So, it should work even while having an horizontal scroll if you set position:relative for .buttons.

Backdrop-filter: Blur not working on a position absolute element

I'm trying to make a tooltip with a glass effect using the backdrop-filter: blur, except that the filter doesn't seem to work on elements in absolute position.
I imagine this is because the item has gone out of the document flow so don't recognize the background, but there might be workarounds to fix it.
image: the filter does not act on elements in absolute position
I found out that elements with transparent background get incorrectly blurred. I wonder if the parent of this element is one that has transparent background.
In my case I had my body transparent which made blur not work, and solved it with:
body {
background-color: white;
}
I can give you a sample which was given by a user in stack overflow. I have given the sample code below Hope it helps:
CSS:
body,
main::before {
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/80625/tree.jpg) 0 / cover fixed;
}
main {
margin: 100px auto;
position: relative;
padding: 10px 5px;
background: hsla(0, 0%, 100%, .3);
font-size: 20px;
font-family: 'Lora', serif;
line-height: 1.5;
border-radius: 10px;
width: 60%;
box-shadow: 5px 3px 30px black;
overflow: hidden;
}
main::before {
content: '';
margin: -35px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
filter: blur(20px);
z-index: -1;
}
HTML:
<main>
<blockquote>"The more often we see the things around us - even the beautiful and wonderful things - the more they become invisible to us. That is why we often take for granted the beauty of this world: the flowers, the trees, the birds, the clouds -
even those we love. Because we see things so often, we see them less and less."
<footer>—
<cite>
Joseph B. Wirthlin
</cite>
</footer>
</blockquote>
</main>

Attach CSS triangle to MatTooltip depending on actual positioning?

I've been wrecking my brain to figure out a way to attach a little CSS triangle to a MatTooltip in Angular that switches from being at the top when the tooltip is displayed below the host element (default) to being at the bottom when the tooltip is above the host element (e.g. when the host element is too close to the bottom of the viewport).
Am I missing some property that would give me a way to switch the CSS
class? Or is it simply impossible?
Any help would be highly appreciated.
It is possible.
Since MatTooltip controls its positioning via transform-origin, you can make use of it.
For example, .mat-tooltip[style*="transform-origin: left center"] only selects tooltips shown to the right of its parent, so you can display the triangle on the left side of the tooltip.
Values of transform-origin:
Tooltip to the right of its parent: left center
Tooltip to the left of its parent: right center
Tooltip above its parent: center top
Tooltip below its parent: center bottom
Welcome to SO!
Although you can use css to draw pointed triangle to mat-tooltip but you cannot move its position according to View-port/DOM view like above or below, So Its not possible to move the pointed triangle.
Solution:
I have used SAT Popover for styling tooltips. You can give it an
angular component and it will display as you style it.
https://ncstate-sat.github.io/popover/
Step 1 - Add the mat CSS class "matTooltipClass="below"" in HTML control.
Step 2 - Add the below CSS class in you CSS file.
.mat-tooltip {
background-color: white;
border-bottom-color: white;
color: black;
margin: 16px;
border-radius: 3px;
outline: none;
box-shadow: 0 0 10px #9f9fa3;
height: auto;
&::after {
width: 0;
height: 0;
content: "";
position: absolute;
border-left: 0.5rem solid transparent;
border-right: 0.5rem solid transparent;
border-bottom: 0.5rem solid rgb(132, 56, 56);
}
&.below {
overflow: initial;
margin-top: 1rem;
&:after {
top: -0.5rem;
right: calc(50% - 0.5rem);
transform: rotate(0);
}
}
&.below[style*="center bottom"] {
overflow: initial;
margin-top: 1rem;
&:after {
top: calc(100%);
right: calc(50% - 0.5rem);
transform: rotate(180deg);
}
}
}

Border radius and background clip not working as expected on pseudo element

I'm trying to create a button that has many layers - consequently I'm having to make use of pseudo elements.
To clarify, my button is actually an anchor <a>.
I've run into an issue with one of the pseudo elements. I'm trying to give it a background while retaining a touchable target outside of the background. In order to do this, I've applied a background-clip over the content.
Everything works apart from the corners, which are applied but just not the shape I was expecting.
Button 2 is the design I'm trying to crack - by ensuring the pseudo perfectly overlays the element.
I now think I know what is happening but don't know why and how to fix it.
The padding is 6px deep, the border radius 10px. The radius is being calculated as 4px deep and 10px wide.
Any advice appreciated.
div {
margin-bottom: 20px;
}
a, a::before {
box-sizing: border-box;
}
a {
background-color: red;
border-radius: 10px;
color: white;
display: inline-block;
height: 36px;
line-height: 36px;
min-width: 100px;
position: relative;
text-align: center;
z-index: 1;
}
a::before {
background-color: rgba(0, 255, 0, 0.5);
border-radius: 10px;
content: "";
display: block;
height: 48px;
padding: 6px 0;
position: absolute;
top: -6px;
left: 0;
width: 100%;
z-index: -1;
}
.button2::before, .button2a::before {
background-clip: content-box;
}
.button2a {
margin-left: 20px;
}
.button2a::before {
background-color: blue;
}
.button3::before {
background-clip: content-box;
border-radius: 50%;
}
<div>
<p>This is a button with no background-clip - border-radius applied as expected<p>
<p><a class="button1">button 1</a></p>
</div>
<div>
<p>This has same border-radius as above, but background-clip applied on content - overlay doesn't completely disappear - leaves odd shapes at corners as can be seen on blue button. I was expecting 10px corners to mirror center</p>
<p><a class="button2">button 2</a><a class="button2a">button 2</a></p>
</div>
<div>
<p>This has same background-clip applied but uses a percentage for border-radius - seems to work as expected</p>
<p><a class="button3">button 3</a></p>
</div>
I'm trying to create a button that has many layers
What about doing things differently and instead of using pseudo element you can rely on multiple background to have multiple layers:
a.button1 {
background:
linear-gradient(to right,transparent 50%,blue 0),
linear-gradient(to bottom,orange 50%,transparent 0),
red;
border-radius: 10px;
color: white;
display: inline-block;
height: 36px;
line-height: 36px;
min-width: 100px;
text-align: center;
}
<a class="button1">button 1</a>

Why doesn't inset box-shadow work over images?

I have a container that uses inset box shadow. The container contains images and text. The inset shadow apparently does not work on images:
The white section here is the container. It contains a white image, and there is inset box shadow applied to it.
body {
background-color: #000000;
}
main {
position: absolute;
bottom: 0;
right: 0;
width: 90%;
height: 90%;
background-color: #FFFFFF;
box-shadow: inset 3px 3px 10px 0 #000000;
}
<main>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Solid_white.png">
</main>
Is there a way to make the inset box shadow overlap images?
Just to chime in on this, because I was just creating something similar...
I hate polluting my markup with extra elements for the sake of styling, so the CSS solution is to use the :after pseudo element:
main::after {
box-shadow: inset 3px 3px 10px 0 #000000;
content: '';
display: block;
height: 100%;
position: absolute;
top: 0;
width: 100%;
}
<main>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Solid_white.png">
</main>
It's probably too late for what you were trying to do, but is the better solution in my estimation.
Because the shadow is part of the parent container it renders below the image. One alternative is to have a div which places a shadow overtop the image like so:
body {
background-color: #BBB;
}
main {
position: absolute;
bottom: 0;
right: 0;
width: 90%;
height: 90%;
background-color: #FFFFFF;
border-radius: 20px;
}
main img {
border-radius: 20px;
}
.shadow {
position: absolute;
width: 100%;
height: 100%;
box-shadow: inset 3px 3px 10px 0 #000000;
border-radius: 20px;
top: 0;
left: 0;
}
<main>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Solid_white.png" />
<div class="shadow"></div>
</main>
Edit: I've updated the fiddle to include border radius on the shadow and on the img which solves the issue identified in the comments.
The reason it's not overlapping is because the image is inside the div, so the image is on top of it. The image is higher (closer to the user) than the div.
You can change the image to use position: relative; z-index: -1, and have the containing div use a border instead of setting background color on the body. You'll need to use box-sizing: border-box to include the border in the width of the div.
DEMO
body {
background-color: #FFF;
}
main {
position: absolute;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
border: 60px solid black;
box-shadow: inset 3px 3px 10px 0 #000000;
box-sizing: border-box;
}
img {
z-index:-1;
position: relative;
}
For those, who're using absolute-positioned, full-size :before/:after pseudo elements, consider using pointer-events: none on the pseudo-element so the original elements remain clickable.
The best way to achieve this in 2020 would be to use mix blend mode on the image. use the box-shadow on the parent element of the img and use mix-blend-mode: multiply.
You could set the image as the div's background instead:
background-image:url(http://www.placehold.it/500x500)
jsFiddle example
https://stackoverflow.com/a/21415060/6235358
that's a great way to do it but we can do it in a better way using the ::after pseudo-class so you'll not have to add an empty <div> to your HTML
As Rilus mentioned we could use a pseudo class. Unfortunately this does not seem to work on an img tag for some reason however we can use a combination of inner and outer containers to achieve the affect we need.
.outer:hover .inner:after {
position: absolute;
content: '';
color: white;
display:block;
bottom: -0px;
right: -0px;
width: 100%;
height: 100%;
z-index: 11;
border: solid 10px red;
}
http://jsbin.com/kabiwidego/1/
not sure about ie 10 though as it seems to handle pseudo classes that are absolutely positioned slightly differently to most browsers.
One simple fix if you are clever with your decimals is to store your content in a separate div which you then select and implement a certain number of pixels from the top.
For example, let's say your header has a height of 50px. You could begin your #content div id 53.45px from the top (or whatever height your drop shadow is) and then your shadow would appear above the images.
One issue with this is that if you are using a rather transparent shadow, the more transarent it is the more tacky it may look by implementing this css.
In practice the code would be as follows:
HTML:
<header>
Whatever's in your header
</header>
<div id="content>
Page content
</div>
CSS:
header {
height: 50px;
box-shadow: 0 5px 5px rgba(0,0,0,1);
}
#content {
top: 55px;
}
Even if i'm late for the party, I had the same issue these days and worked on a solution. For me, the best solution (mobile friendly) is this one:
JSFiddle:
.image-inset-container {
margin-bottom: 30px;
}
.image-inset-shadow {
position: relative;
}
.image-inset-shadow img {
border-radius: 20px;
}
.image-shadow {
position: absolute;
width: 100%;
height: 100%;
box-shadow: inset 3px 3px 10px 0 #000;
border-radius: 20px;
top: 0;
left: 0;
}
<body>
<h4>Reimagined Web Design</h4>
<p>With your input and business goals in mind, we bring your brand to life through custom human-facing graphics and
visual elements targeted toward your audience for good user experience and created in future-forward technology,
guaranteeing a successful new web design.</p>
<div class="image-inset-container">
<div class="image-inset-shadow"><img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Solid_white.png" alt="img1" />
<div class="image-shadow"></div>
</div>
</div>
<p>We initiate a collaborative process where your team is involved in every step to create a frictionless and
delightful
experience for your customers. Our designers immerse themselves in your industry and your brand aesthetic to
deliver
a website that represents your business while achieving your goals for a connected future.</p>
</body>

Resources