How to move arbitrary elements into sidebar responsively using CSS - css

I have a layout where there are N block elements.
These elements stack vertically (as is expected for block elements), this is what I desire for small-screens... however, on larger screens I want to move some of the elements to a sidebar (using CSS only). I'm thinking maybe this is possible with display grid? or some other mix of CSS techniques?
Here is a snippet.. the styling is just to give some color to the divs and show how I'd like it to look.
.want-to-look-like,
.container {
border: 1px solid gray;
padding: 1em;
}
.content div,
.container div {
background-color: #fee;
}
.sidebar div.can-sidebar,
.container div.can-sidebar {
background-color: yellow;
}
.want-to-look-like {
display: flex;
}
.sidebar {
flex-basis: 20%;
}
.content {
flex-grow: 1;
}
<div class='container'>
<div>...</div>
<div class='can-sidebar'>...</div>
<div>...</div>
<div class='can-sidebar'>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div class='can-sidebar'>...</div>
<div>...</div>
</div>
I want the above HTML structure to be styled so it looks like this (on screens wider than 800px, otherwise it would just stay as-is):
<div class='want-to-look-like'>
<div class='content'>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
</div>
<div class='sidebar'>
<div class='can-sidebar'>...</div>
<div class='can-sidebar'>...</div>
<div class='can-sidebar'>...</div>
</div>
</div>
So, the first chunk of HTML (above) is what the HTML structure is.. but on screens larger than 800px I want it to look like the second chunk of HTML (above) having the content/sidebar layout.
I could create a bunch of grid-areas and specifically place each div in its desired grid area(s). However, I'm hoping for some way where I don't have to specify locations for each div manually - rather, divs could be added (dynamically) and they will show in the content or sidebar automatically simply by adding the appropriate class attribute.

You can set the elements to go to the first or second column.
flow dense is needed to avoid leaving holes:
.container {
display: grid;
grid-template-columns: 150px 80px;
grid-gap: 5px;
grid-auto-flow: dense;
}
.container div {
background-color: lightblue;
border: black 1px solid;
grid-column: 1;
}
.container div.can-sidebar {
background-color: yellow;
grid-column: 2;
}
<div class='container'>
<div>...</div>
<div class='can-sidebar'>...</div>
<div>...</div>
<div class='can-sidebar'>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div class='can-sidebar'>...</div>
<div>...</div>
</div>

Related

How to make html elements flow like text

Given I have two (or more) elements, ーlet's use div but they can be span or anything...ー I want the second one to be appended to the first one the same way it would happen with text.
HTML:
<div>First element taking space</div> <div>Second element</div>
What I want:
//<------ 1. parent width big enough -------->
First element taking space Second element
//<---- 2. bit smaller width ------>
First element taking space Second
element
//<- 3. even smaller ->
First element taking
space Second element
What it happens
//<------ 1. parent width big enough -------->
First element taking space Second element
//<---- 2. bit smaller width ------>
First element taking space // even if there's space for the "Second" word in
Second element // the 1st line it starts in the next line
//<- 3. even smaller ->
First element taking // even if there's space for the "Second element" full text
space // it starts in a new line
Second element
This is probably due to the "box" assigned to each element, behaving like this
╔══════════════════════╗
║╔════════════════════╗║
║║First element taking║║
║║space ║║
║╚════════════════════╝║
║╔══════════════╗ ║
║║Second element║ ║
║╚══════════════╝ ║
╚══════════════════════╝
I tried playing with display options (inline, inline-block, flex), white-space (wrap, pre-wrap)... but can't make it work as I want.
Note: the pre-wrap is because I want to preserve spaces as well.
Edit: Added the following snippet with current code:
.root {
display: flex;
align-items: flex-end;
flex-wrap: wrap;
background: pink;
}
.prefix {
white-space: pre-wrap;
}
.text {
white-space: pre-wrap;
}
.colored {
background: red;
}
.big { width: 500px; }
.small { width: 320px; }
.smaller { width: 150px; }
<div class="root">
<div class="prefix">1.> </div>
<div class="text">this is some text of arbitrary width </div>
<div class="text colored">colored text</div>
<div class="text"> more text</div>
</div>
<hr/>
<div class="root small">
<div class="prefix">2.> </div>
<div class="text">this is some text of arbitrary width </div>
<div class="text colored">colored text</div>
<div class="text"> more text</div>
</div>
<hr/>
<div class="root smaller">
<div class="prefix">3.> </div>
<div class="text">this is some text of arbitrary width </div>
<div class="text colored">colored text</div>
<div class="text"> more text</div>
</div>
The solution was very simple and stupid to the point I'm embarrassed: Just use inline elements.
Leaving it here as well in case anyone learning needs it...
.root {
background: pink;
}
.prefix {
display: inline; /* not needed if using native inline tags such as <span> */
white-space: pre-wrap;
}
.text {
display: inline; /* not needed if using native inline tags such as <span> */
white-space: pre-wrap;
}
.colored {
display: inline; /* not needed if using native inline tags such as <span> */
background: red;
}
.big { width: 500px; }
.small { width: 320px; }
.smaller { width: 150px; }
<div class="root">
<div class="prefix">1.> </div>
<div class="text">this is some text of arbitrary width </div>
<div class="text colored">colored text</div>
<div class="text"> more text</div>
</div>
<hr/>
<div class="root small">
<div class="prefix">2.> </div>
<div class="text">this is some text of arbitrary width </div>
<div class="text colored">colored text</div>
<div class="text"> more text</div>
</div>
<hr/>
<div class="root smaller">
<div class="prefix">3.> </div>
<div class="text">this is some text of arbitrary width </div>
<div class="text colored">colored text</div>
<div class="text"> more text</div>
</div>
I don't know if I understood you correctly but I think flexbox would solve your problem, take a look at the snippet below:
.container{
display:flex;
justify-content: center;
align-items: center;
height:100vh;
flex-direction: row;
}
.first-box{
width:auto;
height:auto;
display:flex;
justify-content: center;
align-items: center;
background-color: yellowgreen;
}
.second-box{
width:auto;
height:auto;
display:flex;
justify-content: center;
align-items: center;
background-color: tomato;
}
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<div class="container">
<div class="first-box">This is some text.</div>
<div class="second-box">This is some text but in the second div which has a tomato background-color.</div>
</div>

Aligning child items in a list of flex parents

I am trying to do a very simple two-column layout that is giving me a hard time. I am still learning the art of flex layout so I'm sure there is something simple that I'm missing. I want a vertical list of <div>s, each of which is a flexbox with two child <div>s. The width of first child varies based on content. The second child is flex-grow: 1, and I want those items to left-align across the set of parents. Instead, the first child is sized to content, and the second butts up against it on the right.
#resultsList {
display: flex;
flex-direction: column;
}
.result {
display: flex;
align-items: flex-start;
justify-content: flex-start;
}
.result-text {
flex: 1;
}
<div id="resultsList">
<div class="result">
<div class="result-line">Line 147</div>
<div class="result-text">Blah blah</div>
</div>
<div class="result">
<div class="result-line">Line 223</div>
<div class="result-text">Resukt 2</div>
</div>
<div class="result">
<div class="result-line">Line 445</div>
<div class="result-text">Quick brown fox</div>
</div>
</div>
I have tried many combinations of align, justify, etc. but I always get the same (or a worse) result.
Okay so:
I think is this what you mean right?
There's a lot of flex going on here so the basic principles are:
main-container holds everything, displaying it as flex. Fixed width of 600px.
sub-container is each flex item, being display as column.
first-container is a fixed width (in this case: 150px) and flex-grow: 1;
content does not have a flex grow property, and so is only the width of its content.
padder has a flex-grow property, so it will take up the rest of the remaining space.
second-container takes up the rest of the container.
The rest is just the use of borders. Where applicable you can make border-{top|bottom|left|right} to none, so it appears as if the box is extended out. Try using the chrome dev tools to see the width of each component.
.main-container {
width: 600px;
display: flex;
flex-direction: column;
}
.sub-container {
display: flex;
}
.first-container {
border: 1px solid black;
border-right: none;
flex-grow: 1;
display: flex;
max-width: 150px;
}
.content {
border-right: 1px solid black;
padding: 10px;
}
.padder {
flex-grow: 1;
}
.second-container {
border: 1px solid black;
border-left: none;
flex-grow: 2;
padding: 10px;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="test.css">
</head>
<body>
<div class="main-container">
<div class="sub-container">
<div class="first-container">
<div class="content">
<p>text</p>
</div>
<div class="padder"></div>
</div>
<div class="second-container">
<p>text</p>
</div>
</div>
<div class="sub-container">
<div class="first-container">
<div class="content">
<p>sample text</p>
</div>
<div class="padder"></div>
</div>
<div class="second-container">
<p>text</p>
</div>
</div>
<div class="sub-container">
<div class="first-container">
<div class="content">
<p>more sample text</p>
</div>
<div class="padder"></div>
</div>
<div class="second-container">
<p>text</p>
</div>
</div>
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>

Expand parent DIV to overflowing content width

I've read quite a few similar questions to mine but none is quite the same or has an answer which works for me.
I'm using Twitter Bootstrap 3. I have two rows, and each row contains a col-sm-12 div, so they're the same width. The content in the first row is wider than its container but I have overflow:auto set on the element containing the two rows so a horizontal scrollbar is displayed and the content can be seen using that, so that's fine.
In the second row I have a div to which I'm applying a jQuery plugin (jqxGrid, for what it's worth). I've set the width option of the plugin to be "100%". The resultant grid's content is also too wide for its container but because of the way the jQuery plugin creates the grid it constricts the grid's width to 100% of its parent's width rather than overflowing.
So what I really need is for the .row elements to all be as wide as the widest overflowing content so that when the jQuery plugin evaluates the width of its parent so as to set its own width, the resultant grid ends up being as wide as the overflowing content in the first row.
I've made a fiddle which I hope will illustrate the problem. I feel that at its heart this is a CSS problem so a pure CSS solution would be excellent, but I doubt that that's possible.
.wrapper {
color: #fff;
padding: 10px;
}
.container-fluid {
background-color: #333;
overflow: auto;
}
.row1 {
background-color: yellow;
}
.row2 {
background-color: orange;
}
.short-content {
background-color: red;
width: 100%;
}
.long-content {
width: 2000px;
background-color: blue;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<div class="wrapper">
<div class="container-fluid">
<div class="row row1">
<div class="col-sm-12">
<div class="long-content">
Long content
</div>
</div>
</div>
<div class="row row2">
<div class="col-sm-12">
<div class="short-content">
THe jQuery plugin here is too wide to fit but won't overflow because its width is set to match its parent.
</div>
</div>
</div>
</div>
</div>
To my understanding, wrapping each .col-sm-12 into their own parent .row is a verbose way of having all .col-sm-12 in a single .row container, as .col-sm-12s are always wrapping into a new line.
So, in case your setup allows for removing the intermediate .row tags, the only additional line of css you have to write is float: left; on .row. (In the example below I used the id #custom on .container-fluid to isolate this modification from the rest of your page).
body {
color: #fff;
padding: 10px;
}
.container-fluid {
background-color: #333;
overflow: auto;
}
.row1 {
background-color: yellow;
}
/*.row2 {
background-color: orange;
}*/
.short-content {
background-color: red;
width: 100%;
}
.long-content {
width:2000px;
background-color: blue;
}
#custom .row {
float: left;
}
<div id="custom" class="container-fluid">
<div class="row row1">
<div class="col-sm-12">
<div class="long-content">
Long content
</div>
</div>
<!-- </div> -->
<!-- <div class="row row2"> -->
<div class="col-sm-12">
<div class="short-content">
THe jQuery plugin here is too wide to fit but won't overflow because its width is set to match its parent.
</div>
</div>
</div>
</div>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"/>

Using calc() with a dynamic value?

I am wondering if this is possible: I have a header that can contain a variable amount of text. Below that I have another element which I want to take up the remaining height of the page.
<div class="header row">
<div class="title column large-5">Potentially very long text</div>
<div class="menu column large-7">Menu items</div>
</div>
<div class="content">
</div>
<div class="footer">
</div>
Normally I would do this using calc, eg:
.content {
height: calc(100vh - 75px);
}
Where 75px is the set height of .header.
But in this example, the .header element is dynamic and does not have a set height. Only a padding and font-size are set.
To complicate things, this also uses the Foundation Grid layout, which makes me nervous about using display: table (.title and .menu sit side by side on desktop, but stacked on mobile) .
Is there anyway to get the height of the dynamic header element (without resorting to JQuery)?
You can use flexbox and set .content to flex-grow: 1 so that it will fill to grow the available space.
body {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
.content {
flex-grow: 1;
background: #eee;
}
<div class="header row">
<div class="title column large-5">Potentially very long text</div>
<div class="menu column large-7">Menu items</div>
</div>
<div class="content">
</div>
<div class="footer">
</div>
I made a small pen to show the way to do this using flex box, it involved changing your markup a bit:
css:
.container {
display: flex;
flex-direction: column;
height: 250px; // whatever you want here
}
.header {
width: 100%;
background: red;
padding: 10px;
}
.content {
background: yellow;
width: 100%;
flex-grow: 1;
}
So the content will always take the available space inside the content div.
check the whole pen: http://codepen.io/anshul119/pen/yMYeLa
hope this helps.

Is there a way of writing CSS that lets you re-organize the order of how HTML components appear?

I am working on a responsive site in which the mobile/tablet view differs from the desktop view in the way it re-orders the DIVs.
Is there a way to write maintainable CSS that let's you re-organize the order of how HTML DIVs appear?
For example, the code below controls the order of how DIVs would appear on a desktop device:
<div class="container">
<div class="row1">
<div class="col1A">Sample content</div>
<div class="col2A">Sample content</div>
<div class="col3A">Sample content</div>
</div>
<div class="row2">
<div class="col1B">Sample content</div>
<div class="col2B">Sample content</div>
<div class="col3B">Sample content</div>
</div>
</div>
However, for mobile/tablet view, I want to display the DIVs in different order using CSS, like the example below:
Show row2, col2B
Then row1, col1A
Then row1, col3A
Then row2, col1B
Is this possible using CSS ?
As a proof-of-concept, you can use the flex CSS property to reorder how elements are visually rendered.
In your example, I had to keep the child elements within a single container
and then I could control the order using the order property.
If you want to hide some items on the small screen view, use display: none on the specific items.
Note: For a wide screen, you would need some CSS rules to get the items to look like two rows. (Please specify what you need.)
If you combine this with media queries, you can get a workable solution.
.container {
display: flex;
flex: center;
flex-direction: column;
align-items: center;
border: 1px dotted blue;
}
.container div {
text-align: center;
padding: 10px;
border: 1px dotted gray;
width: auto;
}
.col1A {
order: 2;
}
.col2A {
display: none;
}
.col3A {
order: 3;
}
.col1B {
order: 4;
}
.col2B {
order: 1;
}
.col3B {
display: none;
}
<div class="container">
<div class="row1 col1A">Sample content 1A</div>
<div class="row1 col2A">Sample content 2A</div>
<div class="row1 col3A">Sample content 3A</div>
<div class="row2 col1B">Sample content 1B</div>
<div class="row2 col2B">Sample content 2B</div>
<div class="row2 col3B">Sample content 3B</div>
</div>
If you want to simulate two rows of three elements, you can still use flex with some adjustments. The following may be helpful.
.container {
display: flex;
flex: center;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
border: 1px dotted blue;
padding: 20px 0;
}
.container .row1 {
margin-bottom: 20px;
}
.container div {
text-align: center;
padding: 10px;
border: 1px dotted gray;
flex-basis: calc(33% - 20px);
}
.col1B {
background-color: yellow;
}
<div class="container">
<div class="row1 col1A">Sample content 1A</div>
<div class="row1 col2A">Sample content 2A</div>
<div class="row1 col3A">Sample content 3A</div>
<div class="row2 col1B">Sample content 1B</div>
<div class="row2 col2B">Sample content 2B</div>
<div class="row2 col3B">Sample content 3B</div>
</div>
Set a screen size for mobile device detection in the css and add the following
#media screen and (max-width: SIZE) {
.row2{
display: flex; flex-flow: column;
}
.col1B{
order: 1;
}
.col2B{
order: 2;
}
.col3B{
order: 3;
}
}
And then add the classes to the DIVs
<div class="row2">
<div class="col1B">Sample content</div>
<div class="col2B">Sample content</div>
<div class="col3B">Sample content</div>
</div>
Change order: 1/2/3; to your needs.

Resources