How to center dropdown based on size of parent - html

I have a dropdown-component that when you click the button and dropdown is shown.
I want the triangle on the dropdown to always point to the center of the button that opened it regardless of how big it was.
Is this possible using css?
In the example below I am using a css-variable to achieve this. But in my real scenario I am unable to do this since the width of the container/button is decided by its contents.
Is it possible to reference the width of the container in any other way? I see that percentages used in left-positioning and the transform is based on the width of the menu it self. So that doesn't really help.
body {
margin: 2rem 50%;
--button-size: 100px;
}
.container {
position: absolute;
display: inline-block;
}
.button {
position: relative;
width: var(--button-size);
border: 1px solid black;
}
.button:after {
display: block;
position: absolute;
content: "";
width: 1px;
height: 200%;
top: 0;
left: 50%;
transform: translateX(-50%);
background: red;
}
.item {
white-space: pre;
padding: 1rem;
}
.item + .item {
border-top: 1px solid black;
}
.menu {
padding: 0;
list-style: none;
position: absolute;
border-radius: 4px;
border: 1px solid black;
left: 100%;
transform: translateX(calc(-75% - var(--button-size)/2));
}
.menu:after,
.menu:before {
content: "";
position: absolute;
left: 75%;
transform: translateX(-50%);
width: 0;
height: 0;
border-style: solid;
border-width: 8px;
border-color: transparent;
}
.menu:before {
border-bottom-color: black;
top: -17px;
}
.menu:after {
border-bottom-color: white;
top: -16px;
}
<div class="container">
<div class="button">Centered on me(but using css-variables)</div>
<ul class="menu">
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
</ul>
</div>

I think using a margin: auto; on the element that you want in the center could be useful as it automatically adjusts and places it's element in the center depending on the space available to it in any given situation.

Related

Centre an image on top of a CSS shape?

I am trying to center an image on to a Hexagon made using CSS.
The code I currently have is:
.img-social {
width: 25px;
height: 25px;
}
.hexagon {
position: relative;
width: 35px;
height: 20.21px;
background-color: #525555;
margin: 10.10px 0;
}
.hexagon:before,
.hexagon:after {
content: "";
position: absolute;
width: 0;
border-left: 17.5px solid transparent;
border-right: 17.5px solid transparent;
}
.hexagon:before {
bottom: 100%;
border-bottom: 10.10px solid #525555;
}
.hexagon:after {
top: 100%;
width: 0;
border-top: 10.10px solid #525555;
}
<li class="img-social-container">
<div class="hexagon"></div>
<a><img class="img-social" src="icons/logo-github.png" alt="github"></a>
</li>
I also want the image to be ontop of the hexagon which is another problem I am having.
Just move it? I'm somewhat sure it's not the best solution, but still.
.img-social {
width: 25px;
height: 25px;
position: relative;
bottom: 32.5px;
left: 5px;
margin: 0;
}

Horizontal progess bar with SASS (CSS3)

I'm creating and Angular2 component to achieve the following behavior:
I have an array (strings) in the component named "stepList". This is my view:
<ul class="step">
<li class="step__item selected" *ngFor="let step of stepList">
{{step.label}}
</li>
</ul>
And this is my CSS:
.step{
display: flex;
&__item {
display: block;
padding-left: 25px;
}
}
With the current code, I have the items (names) rendered, but I don't know how to get the line with the circles underneath them.
Thanks in advance!
Use a border-bottom and a pseudo element:
.step{
display: flex;
}
.step__item {
display: block;
position: relative;
padding: 0 12px 25px;
border-bottom: 1px solid lightgray;
}
.step__item::before {
display: block;
position: absolute;
bottom: -5px;
left: calc(50% - 5px);
width: 10px;
height: 10px;
border-radius: 50%;
background: gray;
content: '';
}
<ul class="step">
<li class="step__item selected">
CATEGORIA
</li>
<li class="step__item selected">
ATTRIBUTES
</li>
</ul>
And the SCSS code:
.step{
display: flex;
&__item {
display: block;
position: relative;
padding: 0 12px 25px;
border-bottom: 1px solid lightgray;
&::before {
display: block;
position: absolute;
bottom: -5px;
left: calc(50% - 5px);
width: 10px;
height: 10px;
border-radius: 50%;
background: gray;
content: '';
}
}
}
Padding on the label with a border-bottom to get the line. Create a div with a set height and width (so, for a circle, use the same height and width), forming a square. Add a border-radius of 50% which will make it circular in shape and put the div inside the label. Position absolutely so that it appears over the border-bottom.
http://jsfiddle.net/exjxw7m6/
ul.progress-bar {
height: 300px;
list-style: none;
margin: 0;
padding: 0;
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
overflow: hidden;
&::after {
content: "";
position: absolute;
top:5px;
left:5px;
background: #777;
height: 5px;
width: 100%;
}
li {
background: #000;
border-radius: 100px;
width: 15px;
height: 15px;
z-index: 1;
position: relative;
&.stop ~ li {
background: #777;
&::after {
height: 0;
}
}
&::after {
content: "";
position: absolute;
bottom: 0;
left: 5px;
background: #000;
width: 5px;
height: 100vh;
}
}
}
more info here: (credit) Progress bar made of solid line, with dots as steps

center psuedo element content

I have a little "caret" that is put on my currently hovered menu item ,and I now realize the caret created it not centered. I have a bunch of dynamically sized menu items.
The class menu-caret is applied when hovered by a little bit of jquery, and I have some css like so :
.menu-caret::before {
content:"";
position: absolute;
margin-left: 20px;
bottom: 0;
width: 0%;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid black;
}
See a quick example here - https://jsfiddle.net/keL4zhky/3/.
Is it possible to have the carets be centered on the bottom? Thanks!
EDIT: is it possible to do this without relative? I have a full width sub menu that if i change the ul/li to relative will lose it's effect - See example here http://codepen.io/ajmajma/pen/KgGxWL
Just make the parent position: relative; and add left: 50%; and margin-left: -5px; to the pseudo element:
ul {
width: 100%;
position: relative;
display: inline-block;
list-style-type: none;
}
li {
padding: 20px;
float: left;
position: relative;
}
.menu-caret::before {
content: "";
position: absolute;
left: 50%;
bottom: 0;
width: 0%;
margin-left: -5px;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid black;
}
<ul>
<li class="menu-caret">one</li>
<li class="menu-caret">larger</li>
<li class="menu-caret">larrrrger</li>
</ul>
Instead of left: 50%; and margin-left: -5px;you can also just use left: calc(50% - 5px);
The li has to have position:relative to provide positioning context.
The pseudo-element is then positioned absolutely 50% left and then pulled back 50% of it's own width by the transform. This will work for any size caret and does not require any magic numbers.
ul {
width: 100%;
position: relative;
display: inline-block;
list-style-type: none;
}
li {
padding: 20px;
float: left;
}
.menu-caret {
position: relative;
}
.menu-caret::before {
content: "";
position: absolute;
transform: translateX(-50%);
left: 50%;
bottom: 0;
width: 0%;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid black;
}
<ul>
<li class="menu-caret">one</li>
<li class="menu-caret">larger</li>
<li class="menu-caret">larrrrger</li>
</ul>
Instead of absolute positioning and float, I prefer:
ul {
width: 100%;
position: relative;
display: inline-block;
list-style-type: none;
}
li {
padding: 20px;
text-align:center;
display: inline-block;
}
.menu-caret::after {
text-align: center;
display: block;
content: "";
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid black;
clear: both;
width: 1px;
margin: 0 auto;
}
<ul>
<li class="menu-caret">one</li>
<li class="menu-caret">larger</li>
<li class="menu-caret">larrrrger</li>
</ul>

Unable to align tooltip properly with multiple tooltip message

.tooltip {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
top: -15px;
left: 110%;
}
.tooltip .tooltiptext::after {
content: "";
position: absolute;
top: 50%;
right: 100%;
margin-top: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent black transparent transparent;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
<h2>Right Tooltip w/ Left Arrow</h2>
<div class="tooltip">Hover over me
<ul class="tooltiptext">
<li>Tooltip text 1</li>
<li>Tooltip text 2</li>
<li>Tooltip text 3</li>
</ul>
</div>
Works perfectly with one Tooltip message, but not with multiple message.
It should always be vertically center regardless the no of tooltip messages.
Feel free to ask question if anyone didn't understand the scenario.
Instead of using margin values to adjust / place the toopltip.
Use top:50% and a transform to vertically center by dragging it back up 50% of its own height.
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tooltip {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
margin-top: 50px;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
top: 50%;
transform: translateY(-50%);
left: 120%;
}
.tooltip .tooltiptext::after {
content: "";
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 100%;
border-width: 5px;
border-style: solid;
border-color: transparent black transparent transparent;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
<div class="tooltip">Hover over me
<ul class="tooltiptext">
<li>Tooltip text 1</li>
<li>Tooltip text 2</li>
<li>Tooltip text 3</li>
</ul>
</div>
<br/>
<div class="tooltip">Hover over me
<ul class="tooltiptext">
<li>Tooltip text 1</li>
</ul>
</div>

Creating a div with a pointed side

I want to create a div with an image and text in it that looks like this.
I've managed to get something that looks like this here:
JSFiddle of pointed div
.triangle-down {
background: white;
display: inline-block;
height: 125px;
margin-left: 20px;
margin-bottom: 55px;
position: relative;
width: 200px;
cursor: pointer;
border: red solid 2px;
}
img {
margin: 10px;
}
.triangle-down:before {
border-top: 20px solid red;
border-left: 101px solid transparent;
border-right: 101px solid transparent;
content: "";
height: 0;
left: -1px;
position: absolute;
top: 127px;
width: 0;
}
.triangle-down:after {
border-top: 20px solid white;
border-left: 100px solid transparent;
border-right: 100px solid transparent;
content: "";
height: 0;
left: 0;
position: absolute;
top: 125px;
width: 0;
}
<div class="triangle-down">
<img src="http://placehold.it/180x105">
</div>
The issues I have are:
(1) The curser turns to a pointer outside the shape when it crosses the transparent borders that help create the point. I'd prefer it if the pointer appeared only when inside the visible outline of the shape.
(2) Is there a better way of doing this? I looked at trying to rotate a div to create the point as I thought this would solve the pointer issue but I can't work out how to create an isosceles triangle shape with the correct proportions this way. This would also allow me to apply a border to create the outline rather than overlay two triangles as I have in the JSFiddle. See this post for more on this - Speech bubble with arrow
Here is a version using transform: rotate
/*Down pointing*/
.triangle-down {
background: white;
display: inline-block;
height: 125px;
margin-left: 20px;
margin-bottom: 55px;
position: relative;
width: 200px;
cursor: pointer;
border: red solid 2px;
}
img {
position: relative;
margin: 10px;
z-index: 1
}
.triangle-down:before,
.triangle-down:after {
border-bottom: 2px solid red;
background: white;
content: '';
height: 50px;
left: 5px;
position: absolute;
top: 98px;
width: 54%;
transform: rotate(22deg);
z-index: 0;
}
.triangle-down:after {
left: auto;
right: 5px;
transform: rotate(-22deg);
}
<div class="triangle-down">
<img src="http://placehold.it/180x105">
</div>