What are the calculations behind the properties in a re-rotated element? - css

I have been playing around with an idea of a contact card type of thing. Apparently hexagons are quite a new trend, but I'd like to keep it a tad simpler, namely: rotated squares. Considering I am quite knowledgeable with HTML and CSS, it wasn't that hard to accomplish this. In a few minutes I came up with this.
HTML
<a href="#" title="Profile of Banana">
<span style="background-image: url(http://s5.favim.com/orig/52/portrait-sigma-50mm-f1.4-hsm-canon-eos-5d-mk2-face-Favim.com-473053.jpg);">
Queen Elizabeth
</span>
</a>
CSS
a {
display: inline-block;
margin: 50px;
width: 150px;
height: 150px;
transform: rotate(45deg);
position: relative;
overflow: hidden;
}
/* Pseudo element for border */
a:after {
content: "";
display: block;
width: 142px;
width: calc(100% - 8px);
height: 142px;
height: calc(100% - 8px);
border: 2px solid white;
position: relative;
z-index: 10;
top: 4px;
left: 4px;
}
/* Span for bg-image and text */
a > span {
display: block;
height: 213px;
width: 213px;
top: -31px;
left: -31px;
position: absolute;
background-size: cover;
background-position: center;
transform: rotate(-45deg);
padding: 76px 24px 0;
}
The idea is quite simple:
Make the link itself a block element, rotated it 45 degrees. Don't forget overflow: hidden
Rotate its child 45 degrees back, apply a background-image to this element (dynamically loaded in my case, therefore inline)
This works in all major browsers and degrades gracefully into a simple square in other browsers (IE8 and below; though you might need a background size polyfill). For this reason I want to keep this HTML structure.
So, what's the problem? First of all I'd like to make it applicable to different sizes where I would only need to set the width and height of the link itself (a) after which the height and width of its child are calculated automatically - in my project I can use the relatively new CSS3 calc() function, if that's of any help, along with the beauty of SASS/SCSS. In other words, I need the ratio between the width of a and its child span. As far as I can tell, it seems that the ratio is the square root of 2: 213 / 150 = 1.42. My first question is, then, why? What's the logic and/or arithmetic behind this? Why can't the span simply take up 100% width of its parent? Why need it be exactly square root 2 times more?
Additionally, I also would like to know where the top and left values come from. I haven't figured out yet which arithmetic might be the base of is. I do know that this might be dependent on the value of transform-origin, but I don't know how exactly. In other words, is it possible with a pre-defined transform-origin value to have top and left to be zero and by doing so removing the need for a per-case calculated value? If not, how can the value of these properties be calculated based on the width value of its parent, as that's the only value that should be known?
Summary, if only the width and height of a are known, how do I:
calculate the width for its child, and how can that be explained?
calculate/use the offsets (top and left) on span and how can they be explained?
I do not want to use any JS solutions for this. If something isn't clear, please post a comment so I can clarify.

UPDATED FIDDLE
First of all, as the background-image defined for span will overlap the actual a element, it needs to be bigger. As we are talking about an angle of 45 degrees, this leads to a triangular cut-off shape in which the size of the main square (i.e. the image) is the long side, and the other two equally-sized lines are of the anchor. Therefore, the size of the image (span) should be equal to sqrt(2)*100% ~= 141.42%.
The positioning of the span then. First we rotate the image back by 45 degrees. Import for this action is that the transform-origin is set to 0 0 rather than 50% 50%. By doing so, the element is rotated around a single point right in the top middle of the rhombus. After rotating it's only a matter of translate the element on the X-axis, which can also be done with CSS transforms: translateX(-50%).
No matter what value is now passed onto a's width and height, the image should always be aligned perfectly within it, with the correct dimensions.
(In the fiddle, try giving a a value of, say, 400px. It looks nice, doesn't it? You can also give nice hover effects to the image.)
a {
display: inline-block;
width: 200px;
height: 200px;
transform: rotate(45deg) translate(50%);
position: relative;
overflow: hidden;
}
a:after {
content: "";
display: block;
width: calc(100% - 8px);
height: calc(100% - 8px);
border: 2px solid white;
position: relative;
z-index: 10;
top: 4px;
left: 4px;
}
a > span {
display: block;
height: 141.42%;
width: 141.42%;
top: 0;
left: 0;
position: absolute;
background-size: cover;
background-position: center;
transform: rotate(-45deg) translateX(-50%);
transform-origin: 0 0;
}

Related

How do I disable sub-pixel rendering or force the browser to round properties to the nearest pixel?

My problem as stated in the title is about chrome's sub-pixel rendering. Sometimes you want the browser to determine an element's proper height or width so it takes up all the available space. And that's how floating point values appear. When the numbers after the decimal are high, it seems to get imprecise and make weird spacings. Changing the box-border property doesn't change the result. I made a codepen showing the problem, make sure to use a browser supporting sub-pixel rendering. As you zoom in you can see a space between the border and the pseudo-element.
* {
box-sizing: border-box;
}
div {
position: relative;
width: 100.98px;
height: 100px;
margin-left: 2px;
background-color: aqua;
border-radius: 10px;
border: solid 1px #000;
}
div:after {
position: absolute;
content:'';
width: 15px;
height: 100%;
right: 0;
background-color: black;
}
<div>
Turns out that rounding the width and height doesn't solve the problem at all; it's not about the float values (could be a factor). After some experimenting I found a trick of sorts. If you somehow manage to throw in a right: -1px or a left: -1px to the element's style it will no longer get weirdly spaced out (right: 0 or 1 doesn't work). I tested it with a resizable element. You can move it back into it's original position with transform: translateX(-1px) and there it is. No more subpixel gaps between elements. I made a codepen showcasing the solution: https://codepen.io/m4tex-the-sasster/pen/zYaMdwv

How to do this box copy overlay effect in CSS?

I want to create this dialog window in CSS:
The only way I managed to come close to this was to copy the dialog window several times, tilt it with transform: rotate(..) and play a bit with z-indexes.
Could this be achieved with borders or box shadows without having to copy the original dialog window? It doesn't have to literally be there three times, of course. It can just be an illusion.
I don't think you'd be able to do it with just borders, though you could use pseudo-elements to avoid actually having to copy the element and some Z transforms to achieve this:
#modal, #modal:before, #modal:after{
width: 500px;
height: 300px;
background: whitesmoke;
border-radius: 10px;
box-shadow: 0 0 5px 5px #eee;
content: " ";
position: absolute;
}
#modal:before{
transform: rotate(-3deg) translateZ(-1px);
}
#modal:after{
transform: rotate(-6deg) translateZ(-2px);
}
#modal{
transform-style: preserve-3d;
position: relative;
margin: 50px auto;
}
<div id='modal'></div>
This basically creates two pseudo-copies of your modal and pushes them behind the original with slightly different rotation.

How can I draw a pointed border around an HTML text block?

I tried to draw border around an HTML text block that is arrow-like pointed at the bottom side (turning it into a pentagon), like so:
As I don't know the box's dimensions the irregular border must be as flexible as a standard one. After trying some things with SVG and backgrounds and discarding CSS Shapes for lack of browser support I came up with a solution:
I added an absolutely positioned div element to the original box, drew a regular border around this border-holder on three sides and added the pointed bottom as an SVG background to an ::after-pseudo-element.
.border-holder {
position: absolute;
z-index: -1;
height: calc(100% - 100px);
top: 0;
left: 0;
width: calc(100% + 6px);
border-width: 3px 3px 0;
border-style: solid;
border-color: #f7a522;
}
.border-holder::after {
content: '';
position: absolute;
bottom: -68.5px;
left: -1.5px;
width: calc(100% + 3px);
height: 70px;
background-repeat: no-repeat;
background-size: 100% auto;
background-image: url("data:image/svg+xml;utf-8,%3Csvg ... %3C/svg%3E");
}
See http://codepen.io/wortwart/pen/YWvQWX
This works with some minor flaws (browsers are not very accurate in sizing SVG backgrounds so the borders don't fit always exactly; high zoom levels don't look good). Anyway, I have the nagging feeling that there must be a better solution for such a rather basic requirement. Has anybody an idea how to make this simpler and more robust?

Off by one pixel issue in IE CSS transform

I am using transform: skew to create the effect of a down arrow on my banner image using both the :before and :after tags. The result should look like the following:
However, in IE 9-11 there seems to be a rounding issue. At some heights there is one pixel from the background image that shows below the skewed blocks resulting in the following:
In my case, the banner is a percentage of the total height of the window. Here is the some sample code which should be able to reproduce the problem:
HTML
<div id="main">
<div id="banner"></div>
<section>
<h1>...</h1>
<p>...</p>
</section>
</div>
CSS
#banner {
position: relative;
background-color: green;
width: 100%;
height: 75%;
overflow: hidden;
}
#banner:before,
#banner:after {
content: '';
display: block;
position: absolute;
bottom: 0;
width: 50%;
height: 1.5em;
background-color: #FFFFF9;
transform: skew(45deg);
transform-origin: right bottom;
}
#banner:after {
right: 0;
transform: skew(-45deg);
transform-origin: left bottom;
}
body {
background-color: #333;
position: absolute;
width: 100%;
height: 100%;
}
#main {
max-width: 40em;
margin: 0 auto;
background-color: #FFFFF9;
position: relative;
height: 100%;
}
section {
padding: 0 1em 5em;
background-color: #FFFFF9;
}
And here a working example.
Yes, seems to be a rounding issue – and I don’t know of anything that one could do to fix this. It’s in the nature of percentage values that they don’t always result in full pixel values – and how rounding is done in those cases is up to the browser vendor, I’m afraid.
I can only offer you a possible workaround (resp. “cover up”) that seems to work – if the layout really is as simple as this, and the main content area has a white background, and no transparency or background-image gets involved there.
Pull the section “up” over the banner by a negative margin of -1px (eliminated top margin of h1 here as well, otherwise it adjoins with the top margin of the section – countered by a padding-top), so that its background simply covers up that little glitch:
section {
padding: 1em 1em 5em;
background-color: #FFFFF9;
position:relative;
margin-top:-1px;
}
section h1:first-child { margin-top:0; }
Well, if you look closely, that makes the corner of triangle look slightly “cut off” (by one pixel) in those situations where the rounding glitch occurs – if you can live with that (and your desired layout allows for it), then take it :-) (And maybe serve it to IE only by some means). If not – then sorry, can’t help you there.

div slanted in 2 directions

Is it possible to create the following shape as a DIV in CSS.
The browser support is not important.
You cannot skew an element like this directly, you'll need to use two elements (or generated content) and hide certain overflow to make the flat bottom edge:
http://jsfiddle.net/6DQUY/1/
#skew {
height: 240px;
overflow: hidden;
}
.skew {
background: #000;
display: inline-block;
height: 300px;
width: 500px;
margin-top: 100px;
transform: skew(-8deg, -8deg);
}
Note: I removed the cross browser definitions for better readability.
UPDATE: This would be a more fluid example which resizes in set dimensions: http://jsfiddle.net/6DQUY/3/. Note the padding-bottom on the wrapper which defines the ratio. You may have to play around with the percentage amounts.
#skew {
padding-bottom: 20%;
overflow: hidden;
position: relative;
}
.skew {
background: #000;
position: absolute;
top: 30%;
right: 8%;
left: 8%;
height: 100%;
transform: skew(-8deg, -8deg);
}
Using SVG:
Below is a sample using SVG polygon which can also be scaled easily. Text (if required) can be absolutely positioned on top of the shape.
.shape-svg {
position: relative;
height: 100px;
width: 300px;
}
svg {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
polygon {
fill: black;
}
/* Just for demo*/
.shape-svg{
transition: all 1s;
}
.shape-svg:hover {
height: 200px;
width: 600px;
}
<div class="shape-svg">
<svg viewBox='0 0 100 100' preserveAspectRatio='none'>
<polygon points='5,35 100,0 95,100 0,100' />
</svg>
</div>
The shape can be created using SVG clip path also instead of polygon.
Using CSS and Single Element:
The same shape can be achieved with CSS using only one element also. The key is to set the transform-origin as the side that is required to be straight.
.shape{
height: 100px;
width: 300px;
margin-top: 50px;
background: black;
transform-origin: 0% bottom;
transform: perspective(300px) rotateY(-15deg) skewX(-10deg);
transition: all 1s;
}
.shape:hover{
width: 350px;
height: 150px;
transform: perspective(450px) rotateY(-15deg) skewX(-10deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="shape"></div>
The shape achieved using this method can also be scaled. However as the height of the shape increases, the right side becomes taller and pushes the top-right corner even more higher. So, either the rotation angle needs to be decreased (or) the perspective needs to be increased (shape needs to be moved farther away) for the height of the right side to remain small enough and be within the viewing area. Or else, the margin-top should be increased.
Below is an explanation on why this happens:
Consider a rectangle positioned 300px in front of the viewer's eye. It is being rotated towards to the viewer and as the rotation happens, the side which is getting closer to the user will appear taller than the other side.
We have fixed the transform origin's x coordinate as 0% and so the height of the left side of the shape would be constant and that of the right side would keep increasing based on the rotation angle.
Because the transform origin's y coordinate is bottom, the bottom side of the shape would be kept straight and any height increase on the right side of the element would be projected upwards resulting in the shape going outside of the screen.
There is no such problem if only the width increases because the rotation angle is too minimal and so the shape's right side will never get anywhere close enough to the viewer to look very tall.
The shape in question is not an exact duplicate of the one discussed here but you can get some more ideas by looking at it :)
You could look into CSS transformations (transform) I have created a JsFiddle with a quick example.
HTML
<div class="skew"></div>
CSS
/* Skew the container one way */
.skew {
background: #000;
display: inline-block;
height: 50px;
width: 500px;
margin-top: 100px;
-webkit-transform: skewY(-5deg);
-moz-transform: skewY(-5deg);
-ms-transform: skewY(-5deg);
-o-transform: skewY(-5deg);
transform: skewY(-5deg);
}
NOTE:
You may need to include other transformations to get the unbalanced look.
--EDIT--
Here is another solution but using :before and :after CSS. JsFiddle.

Resources