I have a CSS Framework for personal usage, that I want to be as "automatic" as possible. And i've met a issue that I cannot solve, but maybe you can help me.
Let's say I have 3 cards where one is the top parent and the rest are children of eachother. I'll show what I mean.
<div class="card"> // Top parent
Lorem!
<div class="card"> // First child
Lorem!
<div class="card"> // Second child
Lorem!
</div>
</div>
</div>
My goal is to have it such that the Parent is styled with an opposing background color from the body and the first child to be the same as the body again so it opposes to the parent card. And the second child to be opposed again to the first child, aka the same background as the top parent. And for this to continue infinitely.
It's possible to use set a background colour for each, but instead of doing this manually I wish for this to happen by itself as you add cards inside other cards.
I am currently solving this doing .card > .card but this only works once, and I don't wish to spam the css with .card > .card > .card > .card > .card etc...
I tried using div:nth-child(even) but this doesn't work if you keep doing children inside one another.
How can I make this possible?
Current Code:
SCSS:
#if #{$class} == card {
/* If parent card has a child card */
.#{$class} > .#{$class} {
background-color: var(--clr-light-primary-background);
}
}
CSS:
.card > .card {
background-color: var(--clr-light-primary-background);
}
HTML:
<div class="card column33 sm-rounded">
<div class="card-header">
<h2 class="card-title text-regular text-uppercase">TOP PARENT</h2>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title text-medium">1st CHILD</h2>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title text-medium">2nd CHILD</h2>
</div>
</div>
</div>
</div>
At the moment CSS (AFAIK) does not provide methods to increment CSS vars based on nesting level - why you need to set a finite level of nesting allowed...
but on the other hand would it make sense if your DOM had 20 nested cards ;-)?
Here is a an example that allows 5 nested cards
body {
--color-text: #fff;
--color-back: #272b34;
--color-back-alt: #323743;
color: var(--color-text);
background: var(--color-back);
padding: 1rem;
}
.card {
background: var(--color-back-alt);
padding: 1rem;
}
:not(.card)>.card>.card>.card>.card>.card>.card,
:not(.card)>.card>.card>.card>.card,
:not(.card)>.card>.card {
background: var(--color-back);
}
Body
<div class="card"> 1
<div class="card"> 2
<div class="card"> 3
<div class="card"> 4
<div class="card"> 5
<div class="card"> 6
</div>
</div>
</div>
</div>
</div>
</div>
Can't thnk of a way to do this with CSS that is dynamic enough so here is some JS.
It starts at the outer level card, and then goes on down through them alternating the background color until there are no more.
let odd = true;
let cards = document.querySelectorAll('.card');
do {
cards[0].style.backgroundColor = (odd) ? 'blue' : 'red';
odd = !odd;
cards = cards[0].querySelectorAll('.card');
} while (cards.length > 0)
<div class="card column33 sm-rounded">
<div class="card-header">
<h2 class="card-title text-regular text-uppercase">TOP PARENT</h2>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title text-medium">1st CHILD</h2>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title text-medium">2nd CHILD</h2>
</div>
</div>
</div>
</div>
CSS solution
This is probably not practical for a CSS framework, but you could play around with CSS filters and see if you can get a colour combination that works.
Note the JS here is just for the demo.
document.querySelector("button").addEventListener("click", () => {
const cards = document.querySelectorAll(".card");
const lastCard = cards[cards.length - 1];
lastCard.innerHTML = `${cards.length} <div class="card">${
cards.length + 1
}</div>`;
});
document.querySelector("input").addEventListener("input", (e) => {
const newColor = e.target.value;
document.documentElement.style.setProperty("--card-bg-color", newColor);
});
:root {
--card-bg-color: #666;
}
.card {
padding: .5rem;
border-radius: 0.25em;
background: var(--card-bg-color);
>
}
.card>.card {
filter: invert(100%);
}
/* Demo */
.control {
position: fixed;
bottom: 0;
left: 0;
display: grid;
grid-gap: .5rem;
background: black;
color: white;
padding: 1rem;
}
<div class="card">1
<div class="card">2
<div class="card">3</div>
</div>
</div>
<!-- Demo -->
<div class="control">
<button>Add card</button>
<label for="color">Change colour <input type="color" value="#666666"></label>
</div>
JavaScript solution
const setCardBackgroundColors = () => {
const cards = [...document.querySelectorAll(".card")];
cards && cards.length && cards.forEach((card, index) => {
index % 2 && card.classList.add('has-bg-dark');
})
}
// Just for demo
const template = (level) => {
return `
<article class="card">
<p>${level}</p>
</article>
`
}
document.querySelector("button").addEventListener("click", () => {
const cards = document.querySelectorAll(".card");
const lastCard = cards[cards.length - 1];
lastCard.insertAdjacentHTML('beforeend', template(cards.length + 1));
setCardBackgroundColors();
});
.card {
background-color: #555;
color: white;
padding: 1rem;
border-radius: 0.25em;
position: relative;
}
.card.has-bg-dark {
background-color: #222;
}
/* demo styles */
* {
box-sizing: border-box;
}
body {
font: 18px/1.2 system-ui;
padding: 1rem;
}
.control {
position: fixed;
bottom: 0;
left: 0;
display: grid;
grid-gap: 0.5rem;
background: black;
color: white;
padding: 1rem;
}
<article class="card">
<p>1</p>
</article>
<!-- Demo -->
<div class="control">
<button>Add card</button>
</div>
Related
I would like to inject a independent div element into a table styled: display: table.
The div automatically takes the properties of a table-cell element, especially the width; How do I get it to be the width of 100%?
Here my codepen to test: codepen.io
.wrapper {
margin: 0 auto;
padding: 40px;
max-width: 800px;
}
.herotable {
background-color: #fff;
padding: 1.5em;
box-shadow: 3px 3px 20px rgba(0, 0, 0, .3);
border-radius: 3px;
}
.table {
margin: 0 0 10px 0;
width: 100%;
display: table;
}
.table.list {
display: contents;
}
.row {
display: table-row;
background: var(--colorEven);
/* #f6f6f6; */
}
.row:nth-of-type(odd) {
background: var(--colorOdd);
/* #e9e9e9; */
}
.row.header {
background: #fff;
}
.cell {
padding: 6px 12px;
display: table-cell;
position: relative;
}
.row .cell {
border-top: 1px solid #ddd;
}
.row .cell:last-child {
border-right-width: 0
}
.list .row:last-child .cell {
border-bottom: 1px solid;
}
.row.header .cell {
cursor: pointer;
border: none;
border-bottom: 1px solid;
}
.orders {
display: inline-block;
width: 100%;
margin: 10px 0 10px 0;
border: solid 1px #ccc;
border-radius: 5px;
padding: 10px;
}
.order {}
<div class="wrapper herotable">
<div id="test">
<div class="table">
<div class="row header">
<div class="cell sorting sort" data-sort="name">Name</div>
<div class="cell sorting sort" data-sort="age">Age</div>
<div class="cell sorting sort" data-sort="ocupat">Occupation</div>
<div class="cell sorting sort" data-sort="location">Location</div>
</div>
<div class="table list">
<div class="row">
<div class="cell name" data-title="Name">Luke Peters</div>
<div class="cell age" data-title="Age">25</div>
<div class="cell ocupat" data-title="Occupation">Freelance Web Developer</div>
<div class="cell location" data-title="Location">Brookline, MA</div>
</div>
<div class="orders">
<div class="order">This should be an independent div-element. I would like to inject it by JavaScript when clicking on the line above.<br> It should not expand the name column.</div>
</div>
<div class="row">
<div class="cell name" data-title="Name">Joseph Smith</div>
<div class="cell age" data-title="Age">27</div>
<div class="cell ocupat" data-title="Occupation">Project Manager</div>
<div class="cell location" data-title="Location">Somerville, MA</div>
</div>
<div class="row">
<div class="cell name" data-title="Name">Maxwell Johnson</div>
<div class="cell age" data-title="Age">26</div>
<div class="cell ocupat" data-title="Occupation">UX Architect & Designer</div>
<div class="cell location" data-title="Location">Arlington, MA</div>
</div>
</div>
</div>
</div>
</div>
I do not ask for a JavaScript solution, I do only have a problem with CSS. But JavaScript solutions are welcome.
The easiest means of achieving the layout requirements is to use CSS grid, along with grid-template-columns: subgrid, unfortunately this is (currently) supported only in FireFox version 71+, which represents almost 3%1 of global browser use. As such, while it works, it isn't really viable for a production site in the wild.
That said, the relevant HTML and CSS is below:
// here we cache a reference to all .row elements which are not .header elements,
// using document.querySelectorAll(), which returns a non-live NodeList:
const rows = document.querySelectorAll('.row:not(.header)'),
// declaring a function, using Arrow syntax (since we don't use 'this'
// within the function body:
isVisible = (el) => {
// if el is truthy, so is not 'null' or 'undefined':
if (el) {
// we return the results of the following assessments:
// el.hidden === false: we test whether the hidden property
// evaluates to false; if so:
// window.getComputedStyle(el, null).display !== 'none':
// recovers the rendered 'display' property
// of the element, and the assessment checks
// that it is not equal to 'none';
// if both assessments evaluate to true, we return true otherwise
// we return false:
return el.hidden === false && window.getComputedStyle(el, null).display !== 'none';
}
// by default, in the event of a non-existent element or null-reference,
// is passed to the function we simply return false:
return false;
},
// another function declared using Arrow syntax:
orderToggle = (e) => {
// 'e' is a reference to the Event Object passed from the
// EventTarget.addEventListener() call (later);
// activated is a reference to the element to which the
// event-handler was bound:
const activated = e.currentTarget,
// potentialOrders is a reference to the nextElementSibling
// of the current-target; the variable name refers to the
// fact that there may not be a nextSiblingElement or
// that the nextSiblingElement may not be the appropriate
// element:
potentialOrders = activated.nextElementSibling,
// here we call the isVisible() function, above, to retrieve
// a Boolean (true/false) to represent the current-visibility
// of the potentialOrder element:
currentVisibility = isVisible(potentialOrders);
// if the potentialOrders element is truthy (exists, and does not
// evaluate to either undefined or null), we test whether that
// element matches the CSS selector passed to the Element.matches()
// method:
if (potentialOrders && potentialOrders.matches('.orders')) {
// here we update the hidden property of the potentialOrders
// element, isVisible() function returns true, we wish to hide
// the element, so we set 'hidden' to true, otherwise we set
// it to false (as returned by the isVisible() function):
potentialOrders.hidden = currentVisibility;
}
};
// here we iterate over the NodeList of .row:not(.header) elements
// using NodeList.prototype.forEach() along with an Arrow function:
rows.forEach(
// 'el' is a reference to the current Node of the NodeList over
// which we're iterating:
(el) => {
// here we use EventTarget.addEventListener() to bind the anonymous
// function of the addEventListener() method to:
el.addEventListener('click', function(e) {
// call the orderToggle() function, passing the Event
// Object to the function:
orderToggle(e)
});
});
/* setting common default properties for all elements,
along with the ::before and ::after pseudo-elements: */
*,
::before,
::after {
box-sizing: border-box;
font-size: 1rem;
line-height: 1.5;
margin: 0;
padding: 0;
}
.herotable {
margin: auto 1em;
width: 90vw;
}
/* using CSS grid layout: */
.table {
display: grid;
/* setting four columns of equal width: */
grid-template-columns: repeat(4, 1fr);
}
/* distinguishing the .header element to
make the headings visually distinguishable,
but do consider adding appropriate ARIA
roles/attributes to the HTML for
accessibility purposes: */
.header {
font-weight: bold;
border-bottom: 2px solid currentColor;
}
.table.list {
/* placing this element full width across the
defined grid-columns 1 is the first
column, -1 represents the last column; this
is shorthand for "grid-column-start: 1"
and "grid-column-end: -1": */
grid-column: 1 / -1;
/* setting grid-row-gap of 0.5em, and a
grid-column-gap of 0.25em; to separate
cells (adjust to taste): */
gap: 0.5em 0.25em;
}
.row {
/* using CSS grid-layout: */
display: grid;
/* again positioning the elements
to start in the first column
and end in the last column: */
grid-column: 1 / -1;
/* here we use subgrid to have the
grid of the .row elements match
the columns defined on the parent
element: */
grid-template-columns: subgrid;
}
.row:not(.header) {
/* used to indicate interactivity, this
may or not be desired in your use-case,
adjust to taste: */
cursor: pointer;
}
.orders {
/* we don't need to use subgrid here since
the .orders element(s) are children of a
.table element: */
display: grid;
grid-column: 1 / -1;
}
/* hiding elements with the hidden attribute,
because we've overriden the default
display of the .orders elements: */
.orders[hidden] {
display: none;
}
.order {
border: 1px solid currentColor;
border-left-color: transparent;
border-right-color: transparent;
}
<div class="wrapper herotable">
<div id="test">
<div class="table">
<div class="row header">
<div class="cell sorting sort" data-sort="name">Name</div>
<div class="cell sorting sort" data-sort="age">Age</div>
<div class="cell sorting sort" data-sort="ocupat">Occupation</div>
<div class="cell sorting sort" data-sort="location">Location</div>
</div>
<div class="table list">
<div class="row">
<div class="cell name" data-title="Name">Luke Peters</div>
<div class="cell age" data-title="Age">25</div>
<div class="cell ocupat" data-title="Occupation">Freelance Web Developer</div>
<div class="cell location" data-title="Location">Brookline, MA</div>
</div>
<!-- note that I've set the 'hidden' attribute on the element
so that it is hidden on page-load; this may or may not
be required for your use-case: -->
<div class="orders" hidden>
<div class="order">This should be an independent div-element. I would like to inject it with JavaScript when clicking on line above.<br> It should not expand the name column.</div>
</div>
<div class="row">
<div class="cell name" data-title="Name">Joseph Smith</div>
<div class="cell age" data-title="Age">27</div>
<div class="cell ocupat" data-title="Occupation">Project Manager</div>
<div class="cell location" data-title="Location">Somerville, MA</div>
</div>
<div class="row">
<div class="cell name" data-title="Name">Maxwell Johnson</div>
<div class="cell age" data-title="Age">26</div>
<div class="cell ocupat" data-title="Occupation">UX Architect & Designer</div>
<div class="cell location" data-title="Location">Arlington, MA</div>
</div>
</div>
</div>
</div>
</div>
A version that works in both browsers that don't implement subgrid is below, this is much the same except that column-widths are set using CSS variables and can therefore be accessed, and updated if required, by JavaScript; that possibility is not, however, included in the demo:
const rows = document.querySelectorAll('.row:not(.header)'),
isVisible = (el) => {
if (el) {
return el.hidden === false && window.getComputedStyle(el, null).display !== 'none';
}
return false;
},
orderToggle = (e) => {
const activated = e.currentTarget,
potentialOrders = activated.nextElementSibling,
currentVisibility = isVisible(potentialOrders);
if (potentialOrders && potentialOrders.matches('.orders')) {
potentialOrders.hidden = currentVisibility;
}
};
rows.forEach(
(el) => {
el.addEventListener('click', function(e) {
orderToggle(e)
});
});
:root {
/* setting column-widths for each individual column: */
--column1Width: 25%;
--column2Width: 25%;
--column3Width: 25%;
--column4Width: 25%;
/* I chose to use variables here, too, because some
elements will have to be spaced using margins and
others using gap; this allows consistency in
sizing: */
--horizontalGap: 0.25rem;
--verticalGap: 0.5rem;
/* and this is to ensure that all elements representing
table-rows match the same width: */
--fullWidth: 90vw;
}
/* simple CSS reset as above: */
*,
::before,
::after {
box-sizing: border-box;
font-size: 1rem;
line-height: 1.5;
margin: 0;
padding: 0;
}
/* centering the 'table' on the page,
and assigning its width: */
.herotable {
margin: auto 1em;
width: var(--fullWidth);
}
/* styling the table-header information for
visual distinction: */
.header {
font-weight: bold;
border-bottom: 2px solid currentColor;
}
/* because this is a block-element, and not within
a flex, or grid, element we use margin - along
with the defined custom properties - to maintain
consistent spacing: */
.table.list {
margin-top: var(--verticalGap);
margin-bottom: var(--verticalGap);
}
.row {
/* using CSS flex layout: */
display: flex;
/* justifying the contents of the element
with space-between, to separate them
appropriately based on their size and
the defined gaps: */
justify-content: space-between;
/* assigning the margin between vertically-
adjacent elements: */
margin-top: var(--verticalGap);
margin-bottom: var(--verticalGap);
/* assigning the gaps between adjacent-elements
within the .row elements: */
gap: var(--verticalGap) var(--horizontalGap);
}
.row:not(.header) {
cursor: pointer;
}
/* assigning the flex-basis of each element
.cell element based on its 'column' placement;
this allows any column to be resized via
JavaScript: */
.row .cell:nth-child(1) {
flex: 1 0 var(--column1Width);
}
.row .cell:nth-child(2) {
flex: 1 0 var(--column2Width);
}
.row .cell:nth-child(3) {
flex: 1 0 var(--column3Width);
}
.row .cell:nth-child(4) {
flex: 1 0 var(--column4Width);
}
.order {
border: 1px solid currentColor;
border-left-color: transparent;
border-right-color: transparent;
}
<div class="wrapper herotable">
<div id="test">
<div class="table">
<div class="row header">
<div class="cell sorting sort" data-sort="name">Name</div>
<div class="cell sorting sort" data-sort="age">Age</div>
<div class="cell sorting sort" data-sort="ocupat">Occupation</div>
<div class="cell sorting sort" data-sort="location">Location</div>
</div>
<div class="table list">
<div class="row">
<div class="cell name" data-title="Name">Luke Peters</div>
<div class="cell age" data-title="Age">25</div>
<div class="cell ocupat" data-title="Occupation">Freelance Web Developer</div>
<div class="cell location" data-title="Location">Brookline, MA</div>
</div>
<div class="orders">
<div class="order">This should be an independent div-element. I would like to inject it with JavaScript when clicking on line above.<br> It should not expand the name column.</div>
</div>
<div class="row">
<div class="cell name" data-title="Name">Joseph Smith</div>
<div class="cell age" data-title="Age">27</div>
<div class="cell ocupat" data-title="Occupation">Project Manager</div>
<div class="cell location" data-title="Location">Somerville, MA</div>
</div>
<div class="orders" hidden>
<div class="order">This should be an independent div-element. I would like to inject it with JavaScript when clicking on line above.<br> It should not expand the name column.</div>
</div>
<div class="row">
<div class="cell name" data-title="Name">Maxwell Johnson</div>
<div class="cell age" data-title="Age">26</div>
<div class="cell ocupat" data-title="Occupation">UX Architect & Designer</div>
<div class="cell location" data-title="Location">Arlington, MA</div>
</div>
<div class="orders" hidden>
<div class="order">This should be an independent div-element. I would like to inject it with JavaScript when clicking on line above.<br> It should not expand the name column.</div>
</div>
</div>
</div>
</div>
</div>
JS Fiddle demo.
References:
CSS:
Attribute selectors ([attribute]).
CSS custom properties.
:not().
display.
gap.
margin.
padding.
repeat().
:root.
var() function.
HTML:
hidden attribute.
JavaScript
Arrow functions.
document.querySelector().
document.querySelectorAll().
Element.nextElementSibling.
HTMLElement.hidden.
Logical AND (&&).
NodeList.prototype.forEach().
Node.currentTarget.
Node.target.
Window.getComputedStyle().
Footnotes:
See: https://caniuse.com/css-subgrid.
You need to do it with <table>, <tr> and <td> elements and you can use colspan attribute for a full width cell.
I have an issue where my text isn't moving onto a new line when it reaches the edge of its parent div, but stretches the parent div downwards to make space width-wise. Two pictures are shown below illustrating the issue.
The HTML and appropriate CSS will be shown below.
#import url('https://fonts.googleapis.com/css2?family=Raleway:wght#300&display=swap');
* {
font-family: Raleway;
}
.menu {
display: block;
overflow: hidden;
}
.section-one {
display: inline-block;
}
.section-two {
padding-left: 2em;
}
.block {
padding: 0.5em;
}
.button {
cursor: pointer;
}
.float-left {
float:left;
}
.float-right {
padding-left: 2em;
float:right;
}
.img-menu {
border-radius: 50%;
margin: auto;
display: inline-block;
}
.img-info {
display: inline-block;
}
.h {
padding-left:0.5em;
color: #777;
}
.h-primary {
margin-top: 0;
margin-bottom: 0.25em;
color: black;
}
.h-secondary {
margin-top: 0.25em;
}
.h-tertiary {
margin-top: 0.25em;
margin-bottom: 0;
overflow: auto;
}
<div className='menu'>
<div className='float-left'>
{details.reduce(
function(accumulator, currentValue, currentIndex, array) {
if (currentIndex % 2 === 0) {
var temp = array.slice(currentIndex, currentIndex + 2)
temp[0]['pair'] = true
temp[1]['pair'] = false
accumulator.push(temp)
}
return accumulator;
}, []).map((pair) =>
<div className='block'>
{pair.map((detail) =>
<div onclick="location.href='#';" className={['button', detail.pair ? 'float-left' : 'float-right'].join(' ')}>
<img src={detail.imgsrc} className='img-menu' />
<div className='img-info'>
<div>
<strong>
<h5 className='h h-primary'>
{detail.buttontitle}
</h5>
</strong>
</div>
<div>
<h5 className='h h-secondary'>
{detail.buttonsubtitle}
</h5>
</div>
</div>
</div>
)}
</div>
)}
</div>
<!-- Section with the issue! -->
<div className='section-two float-left'>
<div className='block'>
<div>
<strong>
<h5 className='h h-primary'>
Placeholder
</h5>
</strong>
</div>
<div>
<h5 className='h h-tertiary '>
Placeholder Placeholder Placeholder Placeholder Placeholder Placeholder
</h5>
</div>
</div>
<div className='block'>
<h5 className='h h-primary'>
PlaceholderButton
</h5>
</div>
</div>
</div>
Any help will be greatly appreciated!
I have 2 cards with a class card that are displayed inside a card-stack. There can be N number of cards like this.
<div class="card-stack">
<div class="clear"><button name="clear" value="Clear all" onclick="removeAllCards(this)">Clear All</button></div>
<div class="card" style="--y:1" onclick="remove(this)">
<div class="card-header">
<div class="card-avatar">
<img src="https://static.thenounproject.com/png/363633-200.png">
</div>
<div class="card-title">Announcement 1</div>
</div>
<div class="card-body">
This is announcement 1.
</div>
</div>
<div class="card" style="--y:2" onclick="remove(this)">
<div class="card-header">
<div class="card-avatar">
<img src="https://static.thenounproject.com/png/363633-200.png">
</div>
<div class="card-title">Announcement 1</div>
</div>
<div class="card-body">
This is announcement 1.
</div>
</div>
</div>
When a user hovers on card-stack I apply some CSS to each card
.card-stack:hover .card{
transform: translateY(calc((var(--y) * -105% + 200px)));
box-shadow: none;
}
I would like to apply the HOVER css only if there are more than 1 elements with the class card. How do I do that using CSS?
CodePen for the below snippet
function remove(element) {
element.remove();
}
function removeAllCards(element) {
element.remove();
document.querySelectorAll(".card").forEach(el => el.remove());
}
body {
background-color: #e8eaed;
}
.card {
width: 300px;
min-height: 150px;
background-color: white;
position: fixed;
top: 75vh;
right: 100px;
transform: translate(-50%, -50%);
display: grid;
border-radius: 5px;
font-family: sans-serif;
font-size: 14px;
color: #00000080;
box-shadow: 0 5px 15px 0 #00000040, 0 5px 5px 0#00000020;
transition: transform 200ms;
padding:15px;
cursor:pointer;
}
.card {
transform: translateY(calc((var(--y) * 20px) - 50%)) scale(calc(1.0 + var(--y) * 0.05));
}
.card-avatar {
display:block;
}
.card-avatar img {
width:40px;
height:40px;
float:left;
}
.card-title {
font-size:16px;
display:inline-block;
margin-top:10px;
margin-left:10px;
}
.card-header {
height:30px;
display: inline-block;
}
.card-body {
margin-top:-30px;
}
.card-stack:hover .card{
transform: translateY(calc((var(--y) * -105% + 200px)));
box-shadow: none;
}
.card-stack:hover .clear {
visibility: visible;
transform: translateY(calc((var(--y) * -105% + 200px)));
position:absolute;
top:2vh;
right:40px;
}
.card-stack {
position:fixed;
width: 400px;
min-height: 100vh;
position: fixed;
right: 65px;
}
.clear {
visibility:hidden;
}
<div class="card-stack">
<div class="clear"><button name="clear" value="Clear all" onclick="removeAllCards(this)">Clear All</button></div>
<div class="card" style="--y:1" onclick="remove(this)">
<div class="card-header">
<div class="card-avatar">
<img src="https://static.thenounproject.com/png/363633-200.png">
</div>
<div class="card-title">Announcement 1</div>
</div>
<div class="card-body">
This is announcement 1.
</div>
</div>
<div class="card" style="--y:2" onclick="remove(this)">
<div class="card-header">
<div class="card-avatar">
<img src="https://static.thenounproject.com/png/363633-200.png">
</div>
<div class="card-title">Announcement 1</div>
</div>
<div class="card-body">
This is announcement 1.
</div>
</div>
</div>
One solution would be to target classes that are both first-child and last-child at the same time
.myClass {
color: tomato;
}
.myClass:first-child:last-child {
color: DarkSlateGrey;
}
<div>
<span class="myClass">I'm alone</span>
</div>
<div>
<span class="myClass">We are two</span>
<span class="myClass">We are two</span>
</div>
You can do what Creaforge says and override it for when there is only one .card element, except you'd need to specify the transform and box shadow twice, which means code duplication. And those transform and box shadow values are pretty long.
There are two much cleaner ways to do this:
With a selector. Normally I'd recommend .card-stack:hover .card:not(:nth-child(2):last-child), but level 4 :not() support is still a little incomplete so if you're worried about that you need to do it the old way which looks like this:
.card-stack:hover .card:not(:nth-child(2)), .card-stack:hover .card:not(:last-child) {
transform: translateY(calc((var(--y) * -105% + 200px)));
box-shadow: none;
}
Move the .clear element out of the .card-stack element. There doesn't appear to be a very good reason it needs to be in there. Then change the selector to
.card-stack:hover .card:not(:only-child) {
transform: translateY(calc((var(--y) * -105% + 200px)));
box-shadow: none;
}
(again, outside of level 4 :not() you can't do :not(:first-child:last-child) like Creaforge says, but at the very least CSS gives you :only-child to use)
If your trying to seperate them you can add id="Card1" and id="Card2" and in css just style the id's with #Card1 and #Card2, hope this helps :)
Maybe you can apply :nth-child(n+1) to your card class. It will only select every element after the first one.
See here for more information: https://css-tricks.com/useful-nth-child-recipies/#select-all-but-the-first-five
Maybe you can add a class using javascript instead? That is, renaming the css for hover and applying that class only if card-stack has more than two children. Then utilizing "onmouseover" prop.
<div class="card-stack" onmouseover="onHover(this)">
...
</div>
.card-stack-h:hover .card{
transform: translateY(calc((var(--y) * -105% + 200px)));
box-shadow: none;
}
.card-stack-h:hover .clear {
visibility: visible;
transform: translateY(calc((var(--y) * -105% + 200px)));
position:absolute;
top:2vh;
right:40px;
}
function onHover(stack) {
let cards = stack.querySelectorAll(".card")
if(cards.length > 1) stack.classList.add("card-stack-h")
else stack.classList.remove("card-stack-h");
}
Codepen
How it should work:
Open (toggle .show class) on div.user, and displays the .userSub div.
If I click on another div.user, close (remove .show class) and opens the clicked div.userSub
If I click on the already .show-ed div.user (NOT .userSub), it'd close the target div.user.
Almost works but the problem:
when .userSub div is .show-ed, I can only click to close on the .userSub div, not the .user div. However that would be goal. :)
I've tried to eliminate the problem. Probably the .user selection is wrong and I should use stopPropagation() somewhere, or I should be more specific with the child elements, but I can't figure it out.
let $active
$(document).ready(() => {
$(".user").click(function(e) {
if ($active != null) {
$active.toggleClass("show")
}
$(e.target).children().toggleClass("show")
$active = $(e.target).children()
})
})
.user {
background-color: gray;
padding: 20px;
margin: 5px;
}
.userSub {
display: none;
padding: 20px;
background-color: lightgray;
color: black;
margin: 5px;
}
.show {
display: block;
}
button {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="user">
name 1
<div class="userSub">details 1<button>more 1</button></div>
</div>
<div class="user">
name 2
<div class="userSub">details 2<button>more 2</button></div>
</div>
<div class="user">
name 3
<div class="userSub">details 3<button>more 3</button></div>
</div>
To achieve your goal check that the clicked element was the .user element directly, not a child of it. To do that you can use the target property of the event.
Also note that you can simplify the logic by only applying the .show class to the parent .user and having the CSS rules apply the display: block rule to the child elements based on the class on a parent. Try this:
$(document).ready(() => {
let $users = $(".user").click(function(e) {
if (e.target !== this)
return;
$users.not(this).removeClass('show');
$(this).toggleClass("show")
})
})
.user {
background-color: gray;
padding: 20px;
margin: 5px;
}
.userSub {
display: none;
padding: 20px;
background-color: lightgray;
color: black;
margin: 5px;
}
.user.show .userSub {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="user">
name 1
<div class="userSub">details 1<button>more 1</button></div>
</div>
<div class="user">
name 2
<div class="userSub">details 2<button>more 2</button></div>
</div>
<div class="user">
name 3
<div class="userSub">details 3<button>more 3</button></div>
</div>
so I'm a beginner and I started this project by first writing CSS in styles.scss and then transforming the code inside of it using scss tools. I made an each loop to loop through a set of colors in my color map, placed in a mixin and put that mixin under [class^=btn].
I don't know why this doesn't work?
Here is my SCSS:
//colors
$base-grey: #808080;
$base-white: #ffffff;
$base-green: #71eeb8;
$base-blue: #2dcae6; //base-success: #33c052;
$base-red: #ff6666; //red
$base-orange: #ff751a; //warning
$base-purple: #8a54f7; //info
// grid base class
.grid {
// .grid__row
&__row {
padding: 1em 10px;
display: flex;
flex-direction: column;
// NOTE: replace with media query mixin if aiming for exceeds
#media (min-width: 768px) {
flex-direction: row;
}
}
// .grid__col
&__col {
// create grid columns dynamically
// loop through each column size
#for $i from 1 through 12 {
// concatenate CSS selector, ie when $i = 1,
// selector would be .grid__col--1
&--#{$i} {
// base styles applied to all grid columns
// NOTE: could be converted to a placeholder, along with margin
// from the media query
margin-top: 10px;
flex-basis: 100%;
border: 1px red solid;
// NOTE: replace with media query mixin if aiming for exceeds
#media (min-width: 768px) {
// base stlyes applied to all grid columns
margin-top: 0;
// make column width a percentage of the column number / total columns
flex-basis: #{$i / 12 * 100 + "%"} ;
}
}
}
}
}
// targets all elements with classes that begin with grid__col
[class^=grid__col] {
// grid__col + grid__col, targets two sibling columns
& + & {
// NOTE: replace with media query mixin if aiming for exceeds
#media (min-width: 768px) {
// add grid gutter
margin-left: 10px;
}
}
}
.grid {
&__row {
display: flex;
}
}
//BASE
.container {
text-align: left;
font-family: 'Arial Narrow', Arial,sans-serif;
color: $base-grey;
padding: 5px;
font-weight: 500;
}
p {
text-align: left;
line-height: 1.3;
}
a {
text-decoration: none;
}
//NAVIGATION
.nav {
list-style-type: none;
padding: 0;
text-align: center;
}
.nav__item a {
display: block;
color: inherit;
margin: 8px 0;
padding: 8px;
}
.nav__item a:hover {
color: $base-white;
background-color: $base-green;
}
//TYPOGRAPHY
//link
.link {
color: $base-blue;
font-weight: bold;
}
//blockquote
.blockquote {
border-left: $base-green 8px solid;
padding-left: 10px;
font-style: oblique;
font-size: 1.2em;
}
// headlines
#mixin h2-font-weight {
font-weight: 100;
}
.headline--primary {
color: $base-green;
margin-left: 10px;
}
.headline--secondary {
text-align: left;
#include h2-font-weight;
}
//FORM
.form {
display: flex;
flex-direction: column;
&__input {
border: none;
border-bottom: 2px solid $base-green;
margin-bottom: 15px;
}
&__label--hidden {
display: none;
}
& .headline--secondary {
#include h2-font-weight;
}
}
//BUTTONS
#mixin button-styles {
display: block;
width: 100%;
border: none;
margin-bottom: 15px;
padding: 10px;
color: $base-white;
text-transform: uppercase;
font-weight: bold;
}
$button-colors :(
default:$base-blue,
success:$base-green,
error:$base-red,
warning:$base-orange,
info:$base-purple
);
#mixin button-colors {
#each $button, $color in $button-colors {
.btn--#{$button} {
background-color: #{$color};
}
}
}
[class*=btn] {
#include button-styles;
}
//IMAGE
#mixin center-images {
display: block;
max-width: 100%;
margin: 0 auto;
padding: 8px;
}
[class^=img] {
#include center-images;
}
.img {
&--frame {
border: 2px solid $base-grey;
}
}
This is my HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Circles UI Kit</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/normalize.css">
</head>
<!--
List of classes used
Grid:
.container
.grid__row
.grid__col--1 *NOT USED HERE
.grid__col--2 *
.grid__col--3
.grid__col--4
.grid__col--5
.grid__col--6
.grid__col--7
.grid__col--8
.grid__col--9
.grid__col--10 *
.grid__col--11 *
.grid__col--12
.card
.centered
.theme__colors
(.grid__col--1, .grid__col--2, .grid__col--10, and .grid__col--11 are not used here in the HTML but would be good to include in the Sass)
Typography:
.container
.link
.blockquote
.headline--primary
.headline--secondary
Image:
.img--logo
.img--frame
.img--avatar
Navigation:
.nav
.nav__item
Buttons:
.btn--default
.btn--success
.btn--error
.btn--warning
.btn--info
.theme__colors
Forms:
.form
.form__label--hidden
.form__input
-->
<body class="container">
<div class="grid__row">
<div class="grid__col--3">
<a class="link" href="/">
<img class="img--logo" alt="circles logo" src="images/logo.png">
</a>
</div>
<div class="grid__col--9">
<nav role="navigation">
<ul class="nav">
<li class="nav__item">Typography</li>
<li class="nav__item">Buttons</li>
<li class="nav__item">Form</li>
<li class="nav__item">Images</li>
<li class="nav__item">Grid</li>
</ul>
</nav>
</div>
</div>
<div class="grid__row">
<div class="grid__col--12">
<div class="card">
<p>This is what the navigation menu looks like when the screen is at 769px or higher. When the screen is less
than 769px,
the menu will be displayed vertically.</p>
</div>
</div>
</div>
<div class="grid__row">
<div class="grid__col--8">
<div class="card">
<h4 id="type" class="headline--secondary">Typography</h4>
<h1 class="headline--primary">Take a look at this amazing headline</h1>
<p>This is a typical paragraph for the UI Kit. Here is what a link might look like.
The
typical font family for this kit is Helvetica Neue. This kit is intended for clean and refreshing web layouts.
No jazz hands here, just the essentials to make dreams come true, with minimal clean web design. The kit comes
fully equipped with everything from full responsive media styling to buttons to form fields. <em>I enjoy using
italics as well from time to time</em>.
Fell free to create the most amazing designs ever with this kit. I truly hope you enjoy not only the kit but
this
amazing paragraph as well. :)</p>
<blockquote class="blockquote">You know what really gets me going? A really nice set of block quotes. That's
right, block quotes that say, "Hey,
I'm an article you want to read and nurture."</blockquote>
</div>
</div>
<div class="grid__col--4">
<form class="form">
<legend id="forms" class="headline--secondary">Form Elements</legend>
<img class="img--avatar" src="images/avatar.png" alt="Avatar">
<label class="form__label--hidden" for="username">Username:</label>
<input class="form__input" type="text" id="username" placeholder="Username">
<label class="form__label--hidden" for="password">Password:</label>
<input class="form__input" type="password" id="password" placeholder="Password">
<button class="btn--default theme__colors" type="submit" value="Login">Login</button>
</form>
</div>
</div>
<h4 id="images" class="headline--secondary">Images</h4>
<div class="grid__row">
<div class="grid__col--6">
<img class="img--frame" src="images/image.png" alt="sample image">
</div>
<div class="grid__col--6">
<img class="img--avatar" src="images/avatar.png" alt="Avatar">
</div>
</div>
<h4 id="buttons" class="headline--secondary">Buttons</h4>
<div class="grid__row">
<div class="grid__col--12">
<button class="btn--default theme__colors">default</button>
<button class="btn--success theme__colors">success</button>
<button class="btn--error theme__colors">error</button>
<button class="btn--warning theme__colors">warning</button>
<button class="btn--info theme__colors">info</button>
</div>
</div>
<h4 id="grid" class="headline--secondary">Grid System</h4>
<div class="grid__row">
<div class="grid__col--12 theme__colors">.grid__col--12</div>
</div>
<div class="grid__row">
<div class="grid__col--6 theme__colors">.grid__col--6</div>
<div class="grid__col--6 theme__colors">.grid__col--6</div>
</div>
<div class="grid__row">
<div class="grid__col--4 theme__colors">.grid__col--4</div>
<div class="grid__col--4 theme__colors">.grid__col--4</div>
<div class="grid__col--4 theme__colors">.grid__col--4</div>
</div>
<div class="grid__row">
<div class="grid__col--3 theme__colors">.grid__col--3</div>
<div class="grid__col--3 theme__colors">.grid__col--3</div>
<div class="grid__col--3 theme__colors">.grid__col--3</div>
<div class="grid__col--3 theme__colors">.grid__col--3</div>
</div>
<div class="grid__row">
<div class="grid__col--5 theme__colors">.grid__col--5</div>
<div class="grid__col--7 theme__colors">.grid__col--7</div>
</div>
<div class="grid__row">
<div class="grid__col--8 theme__colors">.grid__col--8</div>
<div class="grid__col--4 theme__colors">.grid__col--4</div>
</div>
<div class="grid__row">
<div class="grid__col--7 theme__colors centered">.centered .grid__col--7</div>
</div>
</body>
</html>
You are missing two things:
The mixin of button colours need to be:
#mixin button-colors {
#each $button, $color in $button-colors {
&.btn--#{$button} {
background-color: #{$color};
}
}
}
with & before .btn.
You didn't call your mixin. You need to write:
[class*=btn] {
#include button-styles();
#include button-colors();
}