Double border with only one element - html

I was trying to get a double bordered (underlined) header. First one is full width, second is just text width. Borders should overlap
There is an easy solution with two elements nested like that:
<h1><span>Title</span></h1>
and css:
h1 {
border-bottom: 1px solid red;
}
h1 span {
display: inline-block;
padding: 0 0 10px;
margin-bottom: -1px;
border-bottom: 1px solid blue;
}
Span has inline-block display property so it has right width.
I'm wondering if it's possible to get same effect with :after, :before selectors and only h1 element.

It can be done. I've used vw units.
Take a look at this Working Fiddle
HTML:
<h1 class="SpecialBorder">Title</h1>
CSS:
*
{
margin: 0;
padding: 0;
}
.SpecialBorder
{
display: inline-block;
position: relative;
}
.SpecialBorder:before , .SpecialBorder:after
{
content:'';
position: absolute;
left: 0;
bottom: 0;
}
.SpecialBorder:before
{
width: 100vw;
border-bottom: 1px solid red;
}
.SpecialBorder:after
{
width: 100%;
border-bottom: 1px solid blue;
}
Explanation:
the before & after pseudo elements are the ones that draw the borders.
both of them are empty elements. with a certain width that causes their border to be visible.
they are absolutely position at the bottom of their <h1> parent.
before: responsible for the red border. so his width is set to '100%' of view port.
after: responsible for the red border. so hes width is set to 100% of his parent (the <h1>), that's why the h1 is set to `display:inline-block;" (so it will span ony just as his content)
vw unit is supported by new browsers only.
notice that if you cant use vw units, you can still make something familiar to that.
delete the display:inline-block; from h1 (causing it to span all the way again)
change the width of before to 100% (to make it span all the way),
change the with of after to some fixed value of your choice.
Edit: as thgaskell stated in th comment,
there's a bug where vw units don't update properly on webkit
browsers when the window is resized.
Edit 2:
for making elements to show after the title, you can use a <br /> tag, or clearing techniques like showed here.

I'm not sure if that's what you want, but you could do these rules:
h1 {
...
}
/* here are the direct children of every h1 */
h1>* {
...
}
::after and ::before selectors would make sense when inserting new content (note the double colons). Here's some MDN on ::after selector and some examples:
https://developer.mozilla.org/en-US/docs/Web/CSS/::after

Related

Complex responsive layout

I have a complex layout design and I want to make it responsive. The problem is I don't know-
How I should approach this design (how to structure the html to achieve this)
flex?
grid?
absolute positioning?
Is it even possible to make this kind of layout without making 2 sets of everything and hiding one of them for desktop and vice versa?
This is the layout that I'm talking about:
Desktop layout
Mobile layout
I don't even need the code part for this implementation.
I just need to know if this is possible and if so how do I do that(which structure, what display type for the parent etc)
If not, what are the work arounds?
Changing the design is the final resort but i really don't want to do that.
Have the three elements as siblings, in the order you want them displayed on mobile.
Detach the blue element's background from its contents (conceptually) - they are not the same thing.
Render the yellow and blue (transparent on desktop) elements as right column (using grid or flex), while placing the blue background under them (I used the parent's :before) for positioning the background.
I purposefully placed an inline red border on the contents of the blue element, to highlight its position.
You're pretty much done. The rest are implementation details.
I challenge you to do it yourself before looking at the solution below.
Key points of the solution below:
Note the required position: relative on the parent and also the position:relative; z-index: 0 on the "right-column" elements (without those rules they're rendered below the :before).
I used min-heights here to size the elements (as they're empty) but, once you place content inside them, you could/should remove the min-heights.
You'll probably need to adjust the margins/paddings to suit your needs, once you place real content inside them, but that should be fairly straight-forward.
Here it is:
body {
margin: 0;
}
* {
box-sizing: border-box;
}
.red {
background-color: #ff8584;
padding: 1rem;
}
.yellow {
background-color: #fff742;
}
.green {
background-color: #c0ff72;
}
.blue {
background-color: #72ccff;
}
.red>* {
min-height: 100px;
margin-bottom: 1rem;
}
.red>*:last-child {
margin-bottom: 0;
}
#media (min-width: 768px) {
.red {
position: relative;
display: grid;
grid-template: 'green yellow' auto 'green blue' 1fr / 1fr 1fr;
min-height: 100vh;
grid-column-gap: 1rem;
}
.red:before {
content: '';
position: absolute;
background-color: #72ccff;
top: 1rem;
right: 1rem;
bottom: 1rem;
left: calc(50% + .5rem);
}
.red>* {
margin-bottom: 0;
}
.yellow {
margin: 1rem 1rem 0;
grid-area: yellow;
}
.green {
grid-area: green;
}
.blue {
grid-area: blue;
}
.yellow,
.blue {
position: relative;
z-index: 0;
}
.blue {
background-color: transparent;
}
}
<div class="red">
<div class="yellow"></div>
<div class="green"></div>
<div class="blue" style="border: 1px solid red"></div>
</div>
I also wrote an SCSS version, making the background properties #mixins and the $spacer a variable, so they could be easily replaced.
Even though #tao answered this question with an better answer, I'd also like to provide a less "professional" answer but works great.
My answer uses grid-template-areas and media queries. In addition to that, I'm not going to select my own answer as accepted answer(Even though this is what I used in the end)
https://codepen.io/absanthosh/pen/WNRqLYQ?editors=1100
I suppose what you could do is:
In the HTML code make the order of elements yellow-green-blue, all three as siblings and direct children of red.
For the mobile version there will be no position settings necessary, no flex or anything, just regular divs with default (= full) width, with some padding and margins.
For the desktop version make green and blue inline-blocks, with a little less than 50% width, all taking into account according paddings and margins. Apply position: relative to red and position: absolute to yellow, which gets a width of around 45% (again, depending on paddings and margins) and a right setting that places it where you want it (as a percentage value). The top setting for yellow depends on the padding/margins of red and blue.

Collapsing margin alignment in Firefox

TLDR: this codepen works fine in Chrome, but the alignment is off in Firefox.
I'm building a jQuery plugin which modifies a text input to give it a dropdown button on the left. In order to get the positioning right, I add a wrapper div, which is the same height as the input, so the button can be absolutely positioned on top of the input, and yet still have the same height:
#wrapper {
position: relative;
}
#overlay {
position: absolute;
top: 0;
bottom: 0;
width: 30px;
}
This works fine until the input has vertical margin: then the container grows to include the margin, and so the dropdown button grows with it. My solution to this was margin collapsing: I gave the input display:block which meant that the container ignored it's margin. All good.
input {
margin: 20px 0 40px; /* testing */
display: block;
}
But now the problem is that by default, inputs are inline elements e.g. you might want to have a submit button next to the input. So I wrapped the whole thing in a container div with display:inline-block, so another inline element like a button can happily sit next to it.
#container {
display: inline-block;
}
This works fine in Chrome, but has weird alignment issues in Firefox when there's any vertical margin on the input. Below I've added the final markup. There's also a codepen link at the top.
<div id="container">
<div id="wrapper">
<input>
<div id="overlay"></div>
</div>
</div>
<button>Submit</button>
Edit: the point is that this is a plugin and I'm trying to work with the user's existing markup and CSS e.g. they have this markup:
<input><button>Submit</button>
and their existing CSS has vertical margin on the input, and I want them to be able to just initialise my plugin on the input and it just work, without forcing them to change their markup/CSS. Now because the plugin needs to add lots of markup around the input (for the overlay and the dropdown list), I wrap it all up in a container div. This container div is the limit of our reach (and does not include the button element, or anything else they choose to put next to their inputs).
To fix this, you'll need to define a line-height in your parent div#test2. Without it, different browsers will give it different values. This will cause Firefox to cause this weird result.
Now, the line-height isn't the only problem, also the vertical-align's baseline value will generate a different result for inline elements than it is for inline-block elements that have a different height than the surrounding inline content. To fix this, change the value to top for the #container element (since that's the inline-block element).
The final result would have the following changed (only pasting the parts that changed):
#test2 {
background-color: green;
line-height:70px;
#container {
// replicate the inline nature of the input
display: inline-block;
vertical-align:top;
}
//the rest of the #test2 nested code
}
That would look like this.
Reply to comment
I've made something that does work by the requirements set. Since you said the extra code (so the divs around the input) are made by the plugin itself, I've taken the liberty of changing that a bit to make this work.
The way it can work quite easily is just not using inline-blocks at all, and sticking with the inline elements. This would change the styles to the following:
#container {
// replicate the inline nature of the input
display: inline;
}
#wrapper {
display: inline;
position: relative;
}
input {
// you'll want to make sure the typed text doesn't appear behind the overlay
padding-left:35px;
}
#overlay {
position: absolute;
top: 0px;
bottom: 0px;
left: 1px;
width: 30px;
background-color: #00C2FF;
}
Notes:
I didn't bother making the overlay cover the full height of the input, since your plugin would just make it a flag anyway. To make it cover the full height, just set negative top and bottom styles on the overlay, equal to the computed padding-top and padding-bottom (resp.) on the input. In this case, you'd have to change them to top:-5px;bottom:-5px;. (you can get the computed style via jQuery's $(input).css('padding-top'))
You could actually also remove the whole #container from it, since the only style it has now is display:inline which really doesn't add anything to the whole thing.
I've added a padding-left to your input, because otherwise you'd have to type behind the overlay, which is just silly.
Is the HTML generated by the plugin and it needs to stay exactly the same? I'm not sure I can figure out exactly why the second example is not working, but you seem to have too many div elements there. You could make since simpler:
HTML
<div id="test1">
<div id="wrapper">
<input>
<div id="overlay"></div>
<button>submit</button>
</div>
</div>
SCSS
input, button {
border: 1px solid black;
padding: 5px;
}
input {
display: inline-block;
padding-left: 35px;
}
#test1 {
background-color: yellow;
padding: 20px 0 40px 0;
#wrapper {
position: relative;
#overlay {
position: absolute;
top: 1px;
left: 1px;
width: 30px;
background-color: #00C2FF;
}
}
}
Codepen example
I've removed the margin, and instead used padding on the parent, it achieves the same thing. You'll also want some padding-left on your input field so the entered text doesn't disappear behind your overlay div.
EDIT: In case you are unable to change the markup:
SCSS:
#test2 {
background-color: green;
#container {
// replicate the inline nature of the input
display: inline-block;
padding: 20px 0 40px 0;
}
#wrapper {
// this is just here to be display:block and ignore the margin on the input
display: block;
position: relative;
}
input {
// tell parent to ignore margin
//display: block;
margin: 0;
}
#overlay {
position: absolute;
top: 1px;
bottom: 1px;
left: 1px;
width: 30px;
background-color: #00C2FF;
}
}
codepen demo
Removed the block and margin declarations from the input field, and moved the spacing to padding of the #container element.
"Disclaimer": Let me just start by saying that I did not find exactly what is causing the problems in Firefox, but I did think of an alternative way you could do it.
The way this works in both Firefox and Chrome is just to use the exact same HTML as you used for your #test1, but on top of that, also using the CSS :before pseudo-element (instead of using the #container and #wrapper). The code I used was:
#test2 {
background-color: green;
position:relative;
&:before {
content:"";
display:block;
position:absolute;
left:1px;
top:1px;
bottom:1px;
margin:20px 0 40px 0;
width:30px;
background:#00C2FF;
}
}
demo
The way this works is to simply position the :before overlay on exactly the same place as the divs previously were. As you can see, I've used most of the same styles as you did, but instead, I've put them on the :before pseudo-class.
Other answers don't know why it doesn't work on Firefox. Well, I think that Firefox has the right behavior and it's a Chrome problem.
In short, you want to align an input with a button. But the input is inside a wrapper. Then, you can use vertical-align to control the vertical aligning between the wrapper and the button, but not between the input and the button.
Here you can see an screenshot with different vertical-align:
See the code.
If you want to align the input and the button (last case in the image), you have a problem, because any of the keywords you can use with vertical-align does that. Only in case that input's top margin and bottom margin are equal, then vertical-align: middle works.
But in general, you have have another solution: vertical-align also accepts a <length> value.
And, to get a perfect alignment, you should use the formula
vertical-align: -(input bottom margin)
Or, if you want to align them even if the button has a bottom margin, then
vertical-align: -(input bottom margin) + (button button margin)
The code formula above works with inline-block <div>, but not with <buttons>.
The formula must be fixed to
vertical-align: -(input bottom margin) -(input offsetHeight)/2 + 6
In your example
(Input bottom margin) = 40px
(Input offsetHeight) = 31px
Then, you need
vertical-align: -(input bottom margin) -(input offsetHeight)/2 + 6
Demo
I could achieve it with the following.Codepen You will have to know the css applied to input and apply it to button as well
button{
position:absolute;
margin-left:5px;
}
input, button {
display: inline-block;
margin: 20px 0 40px 0;
}
please update below in your code.
input, button {
border: 1px solid #000000;
margin: 20px 0 40px;
padding: 5px;
vertical-align: top;
}
hope it will work
http://codepen.io/anon/pen/Isycu

Fix gap that appears on an LI element when using :after

Please take a look at this fiddle http://jsfiddle.net/EWUTX/
These are the styles used:
.box { position: relative; display:inline-block;}
.box:after {
position: absolute;
width: 100%;
height: 10px;
background: green;
content: '';
bottom:-10px;
left:0;
}
I get a small 5px gap when using the style on an li element, but not on a div tag.
If I specify font-size: 0px, the gap goes away. But then all the text within the li disappears.
As the font size of the li increases, the gap widens.
Is there a style to get rid of this gap, without any hard coding of font sizes?
Fiddle again: http://jsfiddle.net/EWUTX/
Thanks
PS: I'm actually building a CSS framework internally where users can specify a status (using classes) like "started", "not-started", etc.
When used, the element should display a small bar below with different colors. Users can use this class on any element.
That gap is part of the line height reserved characters like 'p' letter.
You will get the same gap if you don't set a height to your div. If you want to remove that from an inline element like an img you can set the vertical-align to the bottom:
.box img {
vertical-align: bottom;
}
See: http://jsfiddle.net/DhTzp/
Its the image that crates the whitespace;
img{ display: block; }

extending background horizontally outside div

I have some css:
.note {
background: red;
}
.note > div {
max-width: 780px;
margin: 0px auto;
position: relative;
padding-left: 20px;
border: 1px solid black;
}
.note > div:before {
content: '⚠';
position: absolute;
left: 0px;
}
And a corresponding html like:
<div class='note'><div>Foobar</div></div>
This creates a red line across the screen, but the content will be only in the center area. It works well so far. But I want the whole content to be in a 800px width area, so I add a container:
#container {
max-width: 790px;
margin: 0 auto;
background: green;
border-radius: 10px;
padding: 5px;
}
And some html:
<div id='container'>
<p>Lorem ipsum</p>
<div class="note"><div>foo</div></div>
<p>Foobar</p>
</div>
Of course, note won't work here (the red line doesn't extend beyond the green container). I've been trying to come up with something, but I couldn't. I can't just close the container, place my note, and open another because border-radius and (and also box shadow, but I left it out from the example) would break then. Using a negative margin on .note also doesn't work, because it adds horizontal scrollbars. I could make .note position: absolute;, but then my note would overlap whatever comes after it.
Any ideas how could I solve it?
Update: Here's a JSFiddle. The second version is what I actually want, except that it creates a vertical scrollbar. The third is like Robert's solution, and the only problem is that it takes the div out of flow, and I'd like to avoid hacks like adding a margin-top to the following element because I don't know the length of the note in advance.
.note {
background: red;
position: absolute;
left:0;
right:0;
}
Here's a jsfiddle:http://jsfiddle.net/ySVZb/
Note that I changed some widths so it's easier to see in the jsfiddle screen, but the size is irrelevant. Also note that because I've taken the note div outside the normal flow, you will need to add an appropriate margin to anything that follows or it will fall behind the note div. Some generic like .note + * {margin-top: 2em} will work in some cases, but it will override any margin top already on that element, in those cases you'll need a more specific fix like .note + p {margin-top: 3em;} jsfiddle showing that here: http://jsfiddle.net/ySVZb/1/

Big unremovable bottom padding

The text have a big space in the bottom here: http://jsfiddle.net/qHaFR/
And I am not able to remove it.
Can you tell me how to do it?
The wrapper, in this case <span> needs to be a block element with width and height defined. You'll also need to change the line-height to match the height of the container.
So your style would look like:
#foo {
background-color:yellow;
font-size:260px;
border:1px solid black;
width: 190px; /* if display: block; */
line-height: 200px;
display: block; /* or inline-block */
}
Just to clarify, were you trying to wrap A in an element such as <div> or <h1> you shouldn't need to declare it display: block because div and h1 are already block.
It's because the line-height is actually that big, in order for each character to be displayable there. In some languages that space is fully used. For example, if you'd type ÁĄ, you'd need whole 260px. If you're okay with not being able to display those characters, you'll need to change line-height accordingly and display it as a block:
#foo
{
background-color: yellow;
font-size: 260px;
border: 1px solid black;
line-height: 200px;
display: block; /* or inline-block */
}​
If you're not okay with treating it as a block (it gets 100% width then or you'll need to set it yourself), use display: inline-block;. Also, type ÁĄ instead of A and see that the letters get their top and bottom cut. Here, see this: http://jsfiddle.net/vmVcr/.