CSS - Can I use an object's Width in Calc()? - css

I want something like this:
height: calc(width + 5px);
This is just a small example but I'm ultimately looking for a way to use the set width as a part of the calculation, not a simpler way to solve the example. Any searches I do just gives examples on how to use calc() to set the width. Thanks!

Use a container query. The cqw unit represents 1% of the item's width. You can use that unit in your calc() function.
I made the item resizable in the following demo, to make the functionality easier to observe.
.container {
container-type: inline-size;
background: #ddd;
/* Make resizable, for demo purposes */
resize: horizontal;
overflow: hidden;
}
.item {
background: orange;
}
#container (min-width: 0) {
.item {
/* 50% of container width + 30px */
height: calc(50cqw + 30px);
}
}
<div class="container">
<div class="item">Resize me!</div>
</div>
Note that, as of 2022, container queries are very new and not supported in all browsers. Modern Chrome and Safari are supported, though.

The closest you might be able to come with pure CSS is combining CSS variables with calc
:root {
--width: 100px;
}
div {
border: 1px solid red;
height: calc(var(--width) + 5px);
width: var(--width);
}
<div>
test
</div>
This will let you define a CSS variable --width and use it to calculate a new height for the div.

Related

How can I assign the value of a css calc() to a css-variable, and not have it delay the calculation until the css-variable is used

I have been unable to determine from any explanation I could find, about when the value of a css variable has been set., so I conducted a test
The html
<div class="flex">
<div class="item1">
<div class="included"></div>
</div>
<div class="item2">
<div class="included"></div>
</div>
</div>
the css
:host {
display: grid;
place-content: center;
}
.flex {
display: flex;
flex-direction: column;
width:800px;
height: 300px;
background-color: blue;
--div-width: calc(100% - 10px);
}
.item1, .item2 {
background-color: green;
height: 100px;
}
.item1 {
width: 80%;
}
.item2 {
width: 40%;
}
.included {
width: var(--div-width);
background-color:yellow;
height:50px;
}
Note I did the test inside a custom element, hence the :host descriptor, however that is not important.
What is important is that I set --div-width as calc(100% - 10px) inside a div that is 800px wide - so would expect it to have a value of 790px.
However , from this screenshot
It is obvious that the calculation is being delayed until the place where the variable is used. as the yellow boxes are just 10px short of the surrounding element.
Is there a way of telling css to set the value of the property as the element where it is declared, rather than where it is used.
I did try and proxy the variable - like this ...
--proxy-width: calc(100% - 10px);
--div-width: var(--proxy-width);
but it made no difference.
PS This is a trivial example, how I actually want to use it is to control the width of an item (a textarea) inside a custom element, dependent on the context, so I can make the element responsive to changes of the width of some outer container.
If I understand correctly, you want to make an element responsive to changes in size relative to a parent container. The way I've done this (but relative to the viewport) is by using relative length units like vmin along with calc() and CSS variables. This way, we can create a "responsive unit" whether its relative to the initial containing block or some other positioned ancestor.
The example below shows a <div> with nested <textarea> that is powered by responsive units. Since this width and height calculation is being done relative to the viewport even though the <textarea> is a child of the div. You could possibly swap vmin for a relative length unit which isn't relative to the viewport but relative to some ancestor like the custom element.
.wrapper {
border: 1px solid;
padding: 1rem;
}
textarea {
--w: 150;
--h: 80;
width: 200px; /* fallback width */
height: 200px; /* fallback height */
width: calc(var(--w) * 1vmin);
height: calc(var(--h) * 1vmin);
max-width: 100%;
/* extra styles for demo */
border: 1px solid red;
margin: 0 auto;
}
<div class="wrapper">
<textarea id="demo" name="demo"
rows="5" cols="30">It was a dark and stormy night...
</textarea>
</div>
I found a "perfect" work around!.
The solution to this is to use the 'resize' event to read the size of the element you wanted use 100% on using getBoundingClientRect(); and using the width returned to set the "calc" as width px - whatever
So for the example in the question I would use this code
_resize() {
if (this.resizeInProgress) return;
this.resizeInProgress = true;
requestAnimationFrame(() => {
const container = this.shadowRoot.querySelector('.flex');
const bound = container.getBoundingClientRect();
container.style.setProperty('--div-width', `calc(${bound.width}px - 10px)`);
this.resizeInProgress = false;
});
}
I bind that function to this in my custom element constructor
this.resizeInProgress = true;
this._resize = this._resize.bind(this);
and this in the connectedCallback do
window.addEventListener('resize', this._resize);
this.resizeInProgress = false;
and in my disconnectedCallback remove it again with
this.resizeInProgress = true;
window.removeEventListener('resize', this._resize);
I retain the calc() function because in my real life cases as the amount subtracted is in "em" units

Foundation Flex grid float single element to the left and multiple elements to the right

I have following HTML:
<div>
<div class="elemA"></div>
<div class="elemB"></div>
<div class="elemC"></div>
</div>
I would like to achieve following result on medium breakpoint:
Is there any way to position elements like that, without wrapping B and C into additional parent-container?
Such solution is not an option as element A should be positioned in between B and C on small breakpoint:
It can be easily achieved with regular foundation grid by adding float left and float right styles, however it stops working with flex-grid...
Foundation Float grid is not able to do that (and probably no other flex grids do either). They are simply not designed for such usage. Most FE frameworks provide other grids based on Flex and other techniques, which may or may not give a way to do it.
However once your project uses the Flex grid there's little help in that.
A possible solution is to use custom CSS with floats and source ordering. The only issue is for this, if the height of "B" + "C" is less then the height of "A", you have to know/set the height of your "A" div, because the outer one would only grow to fit "B" and "C" and can cause "A" to overflow other elements coming after the outer div.
/* Core of layout */
.wrapper {
position: relative;
}
#media screen and (min-width: 40em) {
.elemB {
width: 50%;
float: right;
}
.elemA {
top: 0;
position: absolute;
width: 50%;
clear: both;
}
.elemC {
width: 50%;
float: right;
clear: both;
}
}
/* If the height of B + C is less than height of A, unfortunatelly we need to know the height of A */
.elemA {
height: 120px; /* must be known */
}
.wrapper {
min-height: 120px; /* this must be set to the same as the height of A :( */
}
/* Nothing important below this line, only appearance for the example */
.wrapper {
background-color: #bbb;
}
.wrapper div {
color: white;
text-align: center;
}
.elemB {
background-color: #3a598e;
height: 20px; /* simulate some content */
}
.elemA {
background-color: #618745;
}
.elemC {
background-color: #515658;
height: 80px; /* simulate some content */
}
<div class="wrapper">
<div class="elemB">B</div>
<div class="elemA">A</div>
<div class="elemC">C</div>
</div>
This will work regardless of your grid.
Indeed it is not ideal if you'd like to use the breakpoints exactly as defined by your grid, but in fact if you compile your CSS files from the Foundation sources, you can use the media query mixins in your Sass.
If you on the other hand use pre-compiled Foundation CSS, than the breakpoints are fixed and you can simply use the same on your custom CSS. For example to use 1 col layout only on small and two columns above, use #media screen and (min-width: 40em) as in my example above. You can find the media queries of the default breakpoints in the last part of this chapter.

Scale square div according to height keeping ratio [duplicate]

This question already has answers here:
Maintain div aspect ratio according to height
(6 answers)
Closed 6 years ago.
I need to create a square with 100% height and width scaling according to the height, and so that it always keeps its aspect ratio.
Example illustration:
The popular example of a square div scaling is according to width (https://spin.atomicobject.com/2015/07/14/css-responsive-square/). I would like to solve this using CSS/flexbox, but I cannot find a proper solution.
(IE and legacy browser support is not important)
There has to be more than one element sharing the same style.
I've tried drawing it, but I dont know if this makes much sense. The individual squares need to fit the outer divs, but the three squares should not be the same size - rather fit their individual outer div.
I would like to give and idea of how I've tried doing this, but it is difficult to go into details because I've done a lot of things - none of them really working. I've tried absolute positioning, but that requires a width. I've tried calculating the width/height using jquery - this takes waaaay to long - as there are a lot of entries. I've tried using height:100% + width:auto, which renders nothing, I've tried the above solution that I've linked to (which is brilliant, yes sadly is based onthe width as a percent and not the height). etcetcetc!! :)
The solution in the question this has been linked to is not sufficient for my problem.
There is, unfortunately, no straight forward way to implement this in CSS alone. You could use viewport units as others have mentioned, but this is always relative to the entire viewport size. It turns out Javascript is the only way to make it more flexible:
document.addEventListener('DOMContentLoaded', function() {
var el = document.getElementById('square');
(window.onresize = function() {
el.style.width = el.clientHeight + 'px';
})();
});
html, body {
height: 100%;
margin: 0;
}
#container {
background: red;
height: 80%;
}
#square {
background: lime;
height: 100%;
margin: auto;
}
<div id="container">
<div id="square"></div>
</div>
Try this:
width: 100vh;
height: 100vh;
Demo:
body {
margin: 0;
}
.square {
width: 100vh;
height: 100vh;
background: grey;
margin: auto;
}
<div class="square"></div>
jsFiddle
.row {
display:table;
width:100%;
}
.row div {
display:table-cell;
vertical-align:top;
}
.col-1 ,.col-3{
background-color:red;}
.col-2 {
background-color:green;
}
<div class=row>
<div class="col-1">1</div>
<div class="col-2">2</div>
<div class="col-3">3</div>
</div>
You can work with Vh and calc
for this html
<div class="square"></div>
you can use this css
body {
background-color: #515151;
}
.square {
margin: 1vh auto;
background-color: #cacaca;
height: 98vh;
width: calc(99vh * 0.8);
}
you can see how works in this example

How to always center a flexible square in viewport with pure CSS?

I know this question: Height equal to dynamic width (CSS fluid layout)
But I want more!! I want a flexible container which has always the aspect ratio of a square but with max-height and max-width of 100% of the page (the window element) and on the top of it is always vertically and horizontally centered.
Like this:
// Container matches height of the window
// container-height = 100%; container-width = absolute-container-height
-page/browser-window-
- ####### -
- #cont-# -
- #ainer# -
- ####### -
---------------------
// container matches width of the window
// container-width = 100%; container-height = absolute-container-width
--page---
- -
- -
-#######-
-#######-
-#######-
-#######-
- -
- -
---------
Is it possible to achieve this with pure css (and even better cross-browser)?
Edit:
I know there is calc() for css3, but due to the poor mobile browser-support, I don't want to use it.
Edit2:
Seems like, I didn't make myself clear enough. I need height and width of the wrapper to match the height OR the width of the window, depending on which is smaller.The square-container should never exceed the smaller value of the window-height/width.
This is, how it would be done with jQuery:
// container/#main-wrapper has top: 50%, left: 50%, position: absolute via css
$(window).resize(function () {
var $ww = $(window).width();
var $wh = $(window).height();
if ($ww > $wh) {
$('#main-wrapper').css({
height: $wh,
width: $wh,
marginTop : ($wh / 2) * -1,
marginLeft : ($wh / 2) * -1
});
} else {
$('#main-wrapper').css({
height: $ww,
width: $ww,
marginTop : ($ww / 2) * -1,
marginLeft : ($ww / 2) * -1
});
}
});
I finally figured it out. The magic ingredients are the view-port units.
Given this html structure:
.l-table
.l-table-cell
.square
You can use this css (well actuall its scss, but you get the idea) to make it work
html,
body{
height: 100%
}
l-table{
background: orange;
display: table;
height: 100%;
width: 100%;
}
.l-table-cell{
display: table-cell;
vertical-align: middle;
border: 2px solid black;
}
.square{
background: red;
margin: auto;
#media (orientation:landscape) {
width: 70vh;
height: 70vh;
}
#media screen and (orientation:portrait) {
width: 70vw;
height: 70vw;
}
}
http://codepen.io/johannesjo/pen/rvFxJ
For those who need it, there is a polyfill.
EDIT: Since writing the below, I appealed on Twitter and got a response from Brian Johnson. He came up with a solution that isn't 100% perfect semantically, but it's pretty damn good and I'll certainly be using it. He asked that I share it in this discussion. LINK
I'm having the same issue right now and I was just typing out pretty much this exact question, so although I can't answer it, I wanted to share what I've found so far in case it helps anyone come up with the final solution.
To clarify, I need my content to fit into a square which fills 60% of the browser's width if portrait or 60% of the height if landscape.
However, this square must never exceed the width or height of the viewport.
Using the technique found here I've managed to create the fluid square, but it still exceeds the viewport when landscape.
width: 60%;
height:0;
padding-bottom: 60%;
Link to Codepen example
I have tried flipping that technique on it's side for landscape but that doesn't work. (You can see that code in the above example, noted out.)
I can't use a simple max-height property because the height is being worked out by the padding-bottom property.
I've thought about adding an extra div as someone else has suggested (C-Link's post is really interesting) but I can't work out how I'd get it to do what we want it do here.
html
<div id="outer">
<div id="inner">YOUR CONTENTS HERE
</div>
</div>
css
html, body{
height: 100%;
}
#outer{
max-height: 100%;
max-width: 100%;
position: relative;
height: 100%;
}
#inner{
position: relative;
top: 50%;
margin-top: -50% auto auto auto;
background: red;
text-align: center;
color: yellow;
}
See this fiddle.
How about if you take the earlier concept a step further with a similar div as a container. The container has an added max-height & width. I tried this and the container does not throw a scrollbar at me. It is quite interesting in behavior I must say myself. Does this work for you?
<div id="container">
<div id="centered">A DIV</div>
</div>
#container {
top:0;
bottom:0;
right:0;
left:0;
margin:auto;
position:absolute;
width:200px;
height:200px;
max-width:100%;
max-height:100%;
background:#00f;
padding:0;
}
#centered {
background: green;
bottom: 0;
height: 80px;
left: 0;
margin: auto;
position: absolute;
top: 0;
right: 0;
width: 80px;
}
updated fiddle:
http://jsfiddle.net/djwave28/mBBJM/96/

2 floated divs with first div width 100%

I have 2 divs floated left in a container div. The second div has width: 20px. I need the first div to fill all the available space and remains inline. Set first div width to 100% doesn't work because the second div with fixed width goes down. How can i do?
The code is described here: http://jsfiddle.net/7EW5h/4/
Thanks
You can use calc CSS3 function and set a dynamic width to #inner1 div as follows:
width: calc(100% - 20px);
It will be compatible with Firefox 16 (or later) and Internet Explorer 9 (or later).
You can add vendor prefixes as shown:
width: -moz-calc(100% - 20px);
width: -webkit-calc(100% - 20px);
width: calc(100% - 20px);
To make it compatible with Chrome 19 (or later), Firefox 4 (or later), Internet Explorer 9 (or later) and Safari 6 (or later).
You can check compatible tables here: http://caniuse.com/#search=calc
Regarding to you example, I had to set border: 0 to #inner1 and #inner2 divs.
I have tested and worked out a solution in Chrome, IE9, Firefox and Opera:
Use containers for the two input elements.
Change the order of the elements so that the right one is first.
Do not float the element that is supposed to fill the remaining space, just set the display to block (which is the default for div elements).
Set the margin-right of the larger container to the total width of the right element. Here we also need to account for things like borders, margins and paddings of both elements.
HTML:
<div id="container">
<div id="inner2">
<input />
</div>
<div id="inner1">
<input />
</div>
</div>​
CSS:
#inner2 {
float: right;
}
#inner2 input {
height: 20px;
width: 20px;
border: 1px solid #000;
}
#inner1 {
margin-right: 24px;
}
#inner1 input {
width: 100%;
height: 20px;
border: 1px solid #000;
}
Live example: http://jsfiddle.net/7EW5h/22/.
Also note that i have explicitly set borders on the two input elements.
I can not get it to work without changing the HTML or the order of the two elements without using absolute positioning.
Have you tried using position:absolute; to position the elements as you need?
See fiddle - JSFiddle Example
I think, without complicating things, you can do the following.
​Remove the floats from the two inputs.
Absolutely position the second input as shown below.
add padding-right to the first input to avoid content overlap.
also, even if it is not shown in my code below, don't forget the presence of default border, margin and padding.​ ​
#container {
overflow: hidden;
background-color: red;
}
#inner1 {
width: 100%;
background-color: blue;
padding-right:45px;
}
#inner2 {
height: 20px;
width: 20px;
background-color: green;
position:absolute;
right:0;
top:0;
}
​

Resources