Case Examine: lynnandtonic.com 2019 refresh

26 November 2019

Final week I launched my newest portfolio refresh. Just like the earlier two years, I wished to create an expertise that was enhanced by resizing the browser window. The 2017 model gave you a brand new structure each 100 pixels and the 2018 model created a body by body animation.

This yr I initially got down to do one thing with the z-axis and discover depth and ahead/backward movement. I appreciated the thought of utilizing layered illustration to simulate touring by way of house. One thing just like the opening of Magnificence and the Beast, however possibly you journey by way of completely different worlds or by way of tiny doorways like Alice in Wonderland.

What actually acquired me excited although was the idea of Russian nesting dolls. You open one and one thing related, however wholly completely different exists inside.

I began with the thought of a self portrait that cracked open revealing new faces as you scaled the browser. Additional scaling would zoom in, every outer head turning into blurred and ultimately leaving the body as you moved ahead. I hoped it will really feel dynamic, as if it existed in third-dimensional house.

I began implementing this into HTML and CSS to see if it will really feel as I used to be imagining. I set it up with relative widths and heights so the art work would fill your complete browser window. Even earlier than I may add in some delicate transforms and transitions, the browsers screamed out in protest. Safari was like, “Nope!” and actually stopped rendering something.

Soooo… what now?

I attempted issues out with absolute pixel dimensions and issues labored a lot better. Fewer calculations for the browser to make appeared like the way in which to go. So as an alternative of zooming in, possibly at wider widths you possibly can see each face in a wierd, horizontal stack.

Making ready the art work

As I used to be illustrating the completely different faces, I noticed I used to be constrained by the largely oval form of the unique portrait. Every subsequent face is hidden behind the one which precedes it, and to keep up the “reveal” they do want to remain obscured till it’s their flip.

a portrait of the artist, and the same portrait at lower opacity to reveal a skull underneath

This constraint did assist me transfer fairly shortly with illustrations. I used to be capable of finding inspiration in issues I like and artwork types I like. Particularly enjoyable have been the Lichtenstein and Picasso homages.

portraits of the artist, one with comedy mustache and false nose glasses, one in Lichtenstein pop art style, and one in Picasso cubist style

Maintaining the heads largely the identical measurement and form additionally made the structure calculations a lot simpler (although it may possibly look fairly gnarly in my supply recordsdata). I’ll dive into that extra in a bit.

Laying issues out

Every face is made up of a container div and two photos (one for both sides of the face). The markup appears to be like like this:


There are three main types in play to create the opening impact. Every div has a selected min-width and every picture is positioned a selected worth from the left and proper.

So the preliminary blue face will get styling like this:

.face#blue
width: 100vw;
min-width: 620px;

.left
place: absolute;
left: 110px;

.proper
place: absolute;
proper: 110px;

Right here’s a diagram which may assist visualize what that appears like.

a diagram outlining the widths and margins for placement of illustrations within the site

The following face (the cranium) would then have styling that appeared one thing like this:

.face#cranium
width: 100vw;
min-width: 840px;

.left
place: absolute;
left: 220px;

.proper
place: absolute;
proper: 220px;

a similar diagram outlining widths and margins for another illustration

Every subsequent face would get adjusted min-width, left, and proper values so they’re positioned appropriately to create the reveal because the browser scales.

Snap into place

Somewhat element I like is the faces scale and transfer a wee bit once they open. This creates a “snap” impact that provides some dimension.

an animatio showing two sides of a face coming together and moving apart

That is achieved for every face with two media queries in fast succession and CSS transforms.

@media display and (min-width: 621px)
.face#blue .left
.face#blue .proper

@media display and (min-width: 629px)
.face#blue .left
.face#blue .proper

It would look like a small factor, but it surely provides rather a lot.

Shadows and masking

One of the crucial difficult facets of this idea was getting the shadows to behave the way in which I wished.

With the faces overlapping one another, I wished each to solid a shadow on the face under it. CSS masking would make this doable. As you’ll be able to see within the gif under, the shadow ought to solely present on the cranium’s floor, but it surely wanted to be “caught” to the blue face as issues transfer. I’ve the total linear-gradient and the masks in orange displaying on the left and the impact it creates on the correct.

an animation showing the layers of illustration, mask, and shadow

I initially deliberate so as to add the masks to every

and use an :after for the shadow, however there’s a enjoyable browser bug I needed to work round. In Chrome, place: fastened doesn’t work if that ingredient’s father or mother has a remodel utilized (do not forget that snap?). And place: fastened was required to get the impact I wished.

So the markup for every masks ended up like this, as a sibling of the corresponding face.

The left and proper div have the masks utilized. It’s an SVG that’s positioned on the identical left/proper values because the face (on this case, the cranium). An :after pseudo-element attracts the shadow.

.masks

.masks .left
mask-image: url(‘left-skull-mask.svg’);
mask-position: left 220px prime zero;
mask-size: auto 400px;

.masks .proper
mask-image: url(‘right-skull-mask.svg’);
mask-position: proper 220px prime zero;
mask-size: auto 400px;

.masks .left:after
place: fastened;
left: 220px;
background-image: linear-gradient(to proper, rgba(zero,zero,zero,.Three) 50%, clear 57%);

.masks .proper:after

Due to that Chrome bug, I’ve to perform a little little bit of handbook altering to every masks to account for the snap remodel:

@media display and (min-width: 841px)
@media display and (min-width: 849px)

The shadows working on this means provides some depth and dimension to every layer because it strikes in entrance and behind the others.

Pre-processors are great

I’ve the CSS simplified right here to indicate the fundamentals of how issues are working. However in case you have been to take a look at my Stylus file for this web page, issues are arrange a bit in another way. I received’t go too deep into it to save lots of all of our brains, however right here’s a fast overview.

As a result of the calculations have been fairly constant for the completely different faces, I used to be in a position to set variables and create mixins that calculated all the assorted poitioning values for me. So for the face widths, I set variables like this:

$face-1 = 620px
$face-2 = $face-1 + 220
$face-Three = $face-2 + 220
$face-Four = $face-Three + 220
$face-5 = $face-Four + 220

After which my mixin may appear to be this:

face(num,width,width2)
min-width: width
backside: var(–face-y)
z-index: (32 – (num * 2))

img.proper
proper: (100px * num + 10 * num)
img.left
left: (100px * num + 10 * num)

@media display and (min-width: width + 1)
img.proper
remodel: scale(1.07) translate( 6px,zero)
img.left
remodel: scale(1.07) translate(-6px,zero)

@media display and (min-width: width + 9)
img.proper
remodel: scale(1.07) translate( 6px,7px)
img.left
remodel: scale(1.07) translate(-6px,7px)

@media display and (max-width: width2)
opacity: zero

(I’m utilizing a customized property of var(–face-y) right here to place the faces from the underside of the browser for numerous vertical media queries):

:root
@media display and (max-height: 550px)
–face-y: 50px

@media display and (min-height: 551px)
–face-y: 200px

@media display and (min-height: 820px)
–face-y: 400px

@media display and (min-height: 1100px)
–face-y: 570px

However again to that mixin.

I used to be then in a position to create every face with this brief declaration model. Setting issues up like this with :nth-of-type allowed me to alter the order and take away/add faces within the markup without having to regulate any CSS. (That is additionally why the faces and masks are completely different ingredient sorts, divs and spans respectively.)

.face:nth-of-type(1)
face(1,$face-1,zero)
.face:nth-of-type(2)
face(2,$face-2,$face-1)
.face:nth-of-type(Three)
face(Three,$face-Three,$face-2)
.face:nth-of-type(Four)
face(Four,$face-Four,$face-Three)
.face:nth-of-type(5)
face(5,$face-5,$face-Four)

The masks additionally get a mixin (which is a little more sophisticated). Math, amirite?

$shadow-h = 428px

masks(num,title,width,width2)
min-width: width
z-index: (32 – (num * 2) + 1)
backside: var(–face-y)

@media display and (min-width: width + 1)
backside: calc(var(–face-y) – 14px)
@media display and (min-width: width + 9)
backside: calc(var(–face-y) – 22px)

.left,
.proper
min-width: width
@media display and (min-width: width + 1)
top: $shadow-h
mask-size: auto $shadow-h

.left
mask-image: url(‘/belongings/photos/left-‘ + title + ‘-mask.svg’)
mask-position: left (100px * num + 10 * num) prime zero
@media display and (min-width: width + 1)
mask-position: left (100px * num + 10 * (num – 1) – Three) prime zero
&:after
left: (100px * num + 10 * num)

.proper
mask-image: url(‘/belongings/photos/right-‘ + title + ‘-mask.svg’)
mask-position: proper (100px * num + 10 * num) prime zero
@media display and (min-width: width + 1)
mask-position: proper (100px * num + 10 * (num – 1) – Three) prime zero
&:after
proper: (100px * num + 10 * num)

@media display and (max-width: width2)
opacity: zero

With this mixin, I can create every masks with a brief declaration (inside a @helps for good measure).

@helps(mask-image: url(”))
.masks:nth-of-type(2)
masks(2,cranium,$face-2,$face-1)
.masks:nth-of-type(Three)
masks(Three,pizza,$face-Three,$face-2)
.masks:nth-of-type(Four)
masks(Four,pops,$face-Four,$face-Three)
.masks:nth-of-type(5)
masks(5,mustache,$face-5,$face-Four)

There’s some extra enjoyable Stylus stuff happening that made the method enjoyable and manageable for me. If you wish to dig into that, take a peek on GitHub.

Different particulars

There’s rather a lot for me to like and so that you can uncover on this refresh, however I’ll say one in every of my favourite elements is the helmet and cyborg faces combo. I knew I wished to play with transparency someplace and I like how resizing the helmet reveals much more.

illustration of a golden cyborg Lynn and a helmet opening up

And naturally, I like tiny stretchy Lynn on the middle of all of it. The arm stretching was a final minute addition and a superb suggestion from my pal Richard. The left/proper mechanical arms and pulleys couldn’t use my good mixins, so I needed to write one thing further for these. I understand not everybody has a large monitor to see this, however I actually liked it and wished to incorporate it.

a tiny Lynn with stretchy arms holds onto ropes and pulleys

Additionally, vertical media queries + pups. ❤

Numerous great things realized

I at all times be taught one thing new with these refreshes and this one was no completely different.

I acquired to check out masking and uncover all of the bizarre browser points with it (Edge, why you permit artifacts?). I acquired fairly good at positioning and made my mind harm determining repeatable calculation patterns.

I discovered the restrict of what the browser may render whereas resizing. And I gained a greater understanding of after I ought to use CSS customized properties vs pre-processor variables.

Plus I acquired to check out styling the location for darkish mode.

a screenshot of the /web page of lynnandtonic.com with a dark grey background

I’ll finish this with a pleasant reminder that earlier variations of the location are nonetheless viewable within the archive.

Till subsequent yr’s refresh. 👋 Thanks for following alongside!

Leave a Reply