BEM: Best way to have container and row? - css

I have the following setup for a content block in BEM:
SCSS
.container {
width: 960px;
margin: 0 auto;
}
.row {
width: 100%;
float: left;
}
.latest-news__container {
#extend .container;
}
.latest-news__row {
#extend .row;
}
Which compiles to:
.container, .latest-news__container {
width: 960px;
margin: 0 auto;
}
.row, .latest-news__row {
width: 100%;
float: left;
}
My HTML
<div class="latest-news">
<div class="latest-news__container">
<div class="latest-news__row">
<div class="latest-news__image"></div>
<div class="latest-news__content"></div>
</div>
</div>
</div>
Question:
Is using #extend for the .container and .row a good idea in order to keep the class name for the elements as per the block name? Or should I just have .container and .row? Then have another #extend for other blocks and so on.

Take a look at mixes concept (when different blocks or elements are mixed on the same DOM node): https://en.bem.info/methodology/key-concepts/#mix and https://en.bem.info/methodology/css/#mixes
So your markup may look like this (which will give you possibility to avoid #extend at all):
<div class="latest-news container">
<div class="latest-news__item row">
<div class="latest-news__image"></div>
<div class="latest-news__content"></div>
</div>
</div>

Related

Exclude a class and all of its children

I am loading global CSS styles but I need them to not affect one part of the page and all of its subcomponents. There are some old information but is there a solution now when :not() is a Level 4 selector?
Codepen example that is not working: https://codepen.io/LaCertosus/pen/PoaYeRj
I have a HTML structure that is not predefined, I do not know how many and what elements are around and inside the "red" element.
Visual example
<div class="parent">
<div class="_filler">
<div class="_filler">
<div class="block">
Should be red
</div>
</div>
</div>
<div>
<div class="child-lime">
<div class="block">
Should be lime
</div>
<div class="_filler">
<div class="block">
Should be lime
</div>
</div>
</div>
</div>
</div>
.parent:not(.child-lime) {
.block {
background: red;
}
}
/* Block is an example, in reality we don't know the class name */
.block {
height: 100px;
width: 100px;
background: lime;
border: 1px solid #000;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
I have tried different combinations with :not() selector but with no luck. It works when I don't need to include all children.
add this into CSS file
.parent > ._filler .block {
background: red;
}
.child-lime .block {
background: lime;
}
remove this from CSS file
.parent:not(.child-lime) {
.block {
background: red;
}
}
Your question seems to be missing some details, but here's what gets you close (assuming you can't touch the underlying HTML)
.parent {
div:not(.child-lime .block) {
background: red;
}
.block {...}
}
There's an un-classed div element that turns red...but since your comments seem to require not touching the underlying HTML and using the :not pseudo, that's probably as close as you can get.

CSS selector to target element where sibling does not have a specific class

I'm working on a slider that has previous button, content and next button as sibling nodes. I'm hiding disabled buttons and I need to adjust the margin for the content
I'm having difficulties with the selectors. I want to target the container to adjust the margin based on if the button is disabled or not. My initial idea was to do this with flex but it's an old
I've been trying something like
.content {
&:not(+ .button-disabled) {
margin-left: 50px;
}
}
but it seems I'm not allowed to have a +inside :not(). Is there any other way I can target this?
You can define a class to the parent container of the three siblings, like
<div class="disabled-button">
<div class="prev"></div>
<div class="cont"></div>
<div class="next"></div>
</div>
And define CSS for them, like:
.wrapper {
width: 80%;
height: 30px;
display: inline-flex;
}
.wrapper > div {
width: 33%;
height: 100%;
border: 1px solid black;
background-color: green;
}
.wrapper.disabled-button .prev {
margin-left: 50px;
}
<div class="disabled-button wrapper">
<div class="prev"></div>
<div class="cont"></div>
<div class="next"></div>
</div>
<div class="wrapper">
<div class="prev"></div>
<div class="cont"></div>
<div class="next"></div>
</div>

Multiple siblings, place some in same row & fill extra space

I have no control of the html. I have a parent with multiple children.Only some of them must be in the same row, while the rest of them stay unaffected and one of them must take up all the extra space. Content is auto generated and % is not an option.
Other options except inline to place on the same row to avoid the problem are welcome as well.
.parent {
background: red;
}
.same-row-child {
background: green;
display: inline-flex;
}
<div class="parent">
<div class="other-child">A</div>
<div class="same-row-child">B</div>
<div class="same-row-child">C</div>
</div>
To sum up: Α in the first line unaffected.
B+C in the same line with B taking up all the extra space.
If the idea is to use flex, then it should be the parent the flex box:
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
display:flex; display:inline-flex; It enables a flex context for all its direct children.
.parent {
background: red;
display: flex;
flex-wrap: wrap;
}
.other-child {
width: 100%;
}
.same-row-child {
background: green;
}
.parent :last-child {
flex: 1;
margin-left:2px;
}
<div class="parent">
<div class="other-child">A</div>
<div class="same-row-child">B</div>
<div class="same-row-child">C</div>
</div>
looks like not the option you would use See next option
The oldish way is float and overflow, and the one to float is the one that comes first and is supposed to shrink on itself.
see https://css-tricks.com/all-about-floats/
Aside from the simple example of wrapping text around images, floats can be used to create entire web layouts.
.parent {
background: red;
}
.other-child {}
.same-row-child {
float: left;
background: green;
margin-right: 2px;
}
.parent :last-child {
float: none;
overflow: hidden;
margin: 0;
}
<div class="parent">
<div class="other-child">A</div>
<div class="same-row-child">B</div>
<div class="same-row-child">C</div>
</div>

AngularJS: Two-column odd/even layout

Having a problem trying to get a repeating two-column layout in AngularJS. My dataset is a JSON object of image information. I want to show a two column layout of images. No matter what I tweak, something is wrong in my odd/even logic, but I can't seem to figure it out. What am I doing wrong?
.left {
float: left !important;
width: 50% !important;
}
.right {
float: right !important;
width: 50% !important;
}
.group:after {
content:"";
display: table;
clear: both;
}
img {
max-width: 100%;
height: auto;
}
#media screen and (max-width: 480px) {
.left,
.right {
float: none;
width: auto;
}
}
<div ng-repeat="issue in issues">
<div ng-if="$even" class="group">
<div class="left" ng-if="$even">
<img src="{{ issue.image }}" ng-src="{{ issue.image }}">
</div>
<div class="right" ng-if="$odd">
<img src="{{ issue.image }}" ng-src="{{ issue.image }}">
</div>
</div>
</div>
The issue with code is you had wrap your logic inside
<div ng-if="$even" class="group">
Div which is restricting to show odd logic div.
instead of having two different div, I'd say use ngClassEven & ngClassOdd directive. Also remove the wrapper div which has ng-if="$even" condition.
<div ng-repeat="issue in issues">
<div ng-class-even="'left'" ng-class-odd="'right'">
<img ng-src="{{ issue.image }}">
</div>
</div>
I guess you already got your answer, but still here are some alternatives which may prove useful:
Simply ng-class - it's a little more flexible, so you may find it useful in other cases too. In this case:
<div ng-repeat="issue in issues" ng-class="{left: $even, right: $odd}">
<img ng-src="{{ issue.image }}">
</div>
or
<div ng-repeat="issue in issues" ng-class="$even ? 'left' : 'right'">
<img ng-src="{{ issue.image }}">
</div>
Note that unlike some other properties ng-class can coexist with class in harmony so you could also add class="item" or something similar.
Since it's a styling issue you may want to try to solve it in css. As long as you think IE 6-8 should die you can use the nth-child selector:
:nth-child(odd) { ... }
:nth-child(event) { ... }
Also since both my and Pankaj's answers removed your group class here is some simpler css which you could use instead:
.item {
float: left;
width: 50%;
}
.left {
clear: left;
}
#media screen and (max-width: 480px) {
.item {
float: none;
width: auto;
}
}
Or again if you're not all about IE you could use flexbox (which removes the need for any JS):
.parent {
display: flex;
flex-wrap: wrap;
}
.item {
flex: 0 0 50%;
}
#media screen and (max-width: 480px) {
.item {
flex-basis: 100%;
}
}

Using CSS, how to add a pseudo element before every odd child element that is "outside" of that child element?

I want to create a grid with two columns whose width will be equal. My base HTML code looks like this:
<div class="linkgrid">
<div class="gridentry">
Loooooooooooooong
</div>
<div class="gridentry">
Short
</div>
<div class="gridentry">
Meeeedium
</div>
</div>
In this example, the first and the second gridentry should lie in the the first row. The thrid gridentry should lie in the second row. All gridentrys should have the same width.
~~~
I came up with a solution that uses a CSS table. However, to make sure the row "breaks" after every second cell, it currently requires non-semantic elements to force these "row breaks":
.linkgrid {
display: table;
border-spacing: 2px;
table-layout: fixed;
width: 50%;
}
.gridentry {
display: table-cell;
background-color: red;
padding: 5px;
text-align: center;
}
.gridentry a {
color: white;
}
.THIS-SHOULD-BE-A-PSEUDO-ELEMENT-BEFORE-EVERY-ODD-CHILD {
/* I imagine a selector that looks somewhat like this:
.linkgrid .gridentry:nth-child(odd):outsidebefore {
*/
display: table-row;
}
<div class="linkgrid">
<span class="THIS-SHOULD-BE-A-PSEUDO-ELEMENT-BEFORE-EVERY-ODD-CHILD"></span>
<div class="gridentry">
Loooooooooooooong
</div>
<div class="gridentry">
Short
</div>
<span class="THIS-SHOULD-BE-A-PSEUDO-ELEMENT-BEFORE-EVERY-ODD-CHILD"></span>
<div class="gridentry">
Meeeedium
</div>
</div>
Is there a way to remove my <span>s from my HTML (because they do not have any semantics) and use a clever CSS selector that adds them as pseudo elements at the right positions instead?
I do know that :before will "create" a pseudo-element within the selected element. Is there a non-JavaScript, CSS-only way to add a pseudo-element outside of the selected element like required in this example?
Another edit: For all those familiar with the Chrome developer tools, I want my result to look somewhat like this in the DOM tree:
<div class="linkgrid">
::outsidebefore
<div class="gridentry">
Loooooooooooooong
</div>
<div class="gridentry">
Short
</div>
::outsidebefore
<div class="gridentry">
Meeeedium
</div>
</div>
...where the ::outsidebefore pseudo-elements should have the CSS property display: table-row;.
Update 2016-01-04: While this specific question remains unanswered, my original problem was solved another way: https://stackoverflow.com/a/34588007/1560865
So please only post replies to this question that answer precisely the given question.
Display Level 3 introduces display: contents:
The element itself does not generate any boxes, but its children and
pseudo-elements still generate boxes as normal. For the purposes of
box generation and layout, the element must be treated as if it had
been replaced with its children and pseudo-elements in the document
tree.
Then, you can:
Wrap each cell in a container element
Set display: contents to those containers
Add ::before or ::after pseudo-elements to those containers
The result will look like as if the pseudo-elements were added to the cell, but outside it.
.wrapper {
display: contents;
}
.wrapper:nth-child(odd)::before {
content: '';
display: table-row;
}
.linkgrid {
display: table;
border-spacing: 2px;
table-layout: fixed;
width: 50%;
}
.wrapper {
display: contents;
}
.wrapper:nth-child(odd)::before {
content: '';
display: table-row;
}
.gridentry {
display: table-cell;
background-color: red;
padding: 5px;
text-align: center;
}
.gridentry a {
color: white;
}
<div class="linkgrid">
<div class="wrapper">
<div class="gridentry">
Loooooooooooooong
</div>
</div>
<div class="wrapper">
<div class="gridentry">
Short
</div>
</div>
<div class="wrapper">
<div class="gridentry">
Meeeedium
</div>
</div>
</div>
Note display: contents is not widely supported yet, but works on Firefox.
The most straightforward way is using an actual table structure. That is, one table divided into rows, in which the entries sit.
Also, you had width:50% on the table, but I believe from the question text that you meant every table cell to be 50% wide, rather than the table taking up 50% of the window width; so I corrected that.
.linkgrid {
display: table;
border-spacing: 2px;
}
.gridrow { /* new */
display: table-row;
}
.gridentry {
display: table-cell;
background-color: red;
padding: 5px;
text-align: center;
width: 50%; /* moved */
}
.gridentry a {
color: white;
}
<div class="linkgrid">
<div class="gridrow">
<div class="gridentry">
Loooooooooooooong
</div>
<div class="gridentry">
Short
</div>
</div>
<div class="gridrow">
<div class="gridentry">
Meeeedium
</div>
</div>
</div>

Resources