Published

Using CSS, HTML, and maybe a little logic to display images with a consistent surface area

Every once in a while, I have to figure out how to display images on the web with a consistent surface area. It’s usually in relation to making a lot of logos with very different aspect ratios look evenly sized so that none of them stick out like a sore thumb.

I haven’t had to do this in a while but came across a tweet by Nick Sherman that prompted me to think about it again.

To achieve an evenly-sized group of images, you have to calculate a maximum width that is proportionate to the surface area you want. To do this, you need to be able to calculate a square root, you need the width and height of each original image, and all of your images need to be tightly cropped since extra negative space will throw things off visually.

In the past, I’ve achieved this with logic since vanilla CSS doesn’t currently support a sqrt() function (more on this later). I’ve usually used PHP since I tend to work with Kirby CMS pretty often.

The function in PHP:

function max_img_width($img_width, $img_height, $ideal_area) {
  $max_width = round($img_width * sqrt($ideal_area / ($img_width * $img_height)));
  echo $max_width;
}

And the function in use + corresponding HTML:

<img style="max-width: <?php max_img_width(1500, 3000, 40000); ?>px;" src="https://piperhaywood.com/my-image.jpg">

A CSS-only solution that works now

There seems to be a CSS-only way to go about this though. Apparently you can approximate square roots in CSS by using a series of CSS variables and calc(), see more info in this thread.

I will warn you: this is ugly. Also, it heavily relies on CSS variables which may or may not work for you depending on your browser support requirements. Here’s the CSS and the image markup:

img {
  --width: 0;
  --height: 0;
  --ideal-area: 40000;
  --area: calc(var(--width) * var(--height));
  --ratio: calc(var(--ideal-area) / var(--area));
  --guess01: calc(calc(var(--ratio) + calc( var(--ratio) / var(--ratio))) / 2);
  --guess02: calc(calc(var(--guess01) + calc( var(--ratio) / var(--guess01))) / 2);
  --guess03: calc(calc(var(--guess02) + calc( var(--ratio) / var(--guess02))) / 2);
  --guess04: calc(calc(var(--guess03) + calc( var(--ratio) / var(--guess03))) / 2);
  --guess05: calc(calc(var(--guess04) + calc( var(--ratio) / var(--guess04))) / 2);
  --guess06: calc(calc(var(--guess05) + calc( var(--ratio) / var(--guess05))) / 2);
  --guess07: calc(calc(var(--guess06) + calc( var(--ratio) / var(--guess06))) / 2);
  --guess08: calc(calc(var(--guess07) + calc( var(--ratio) / var(--guess07))) / 2);
  max-width: calc(var(--width) * var(--guess08) / 2 * 1px);
}
<img style="--width: 1500; --height: 3000;" src="https://piperhaywood.com/my-image.jpg">

I’d want to do some more browser testing since this results in a pretty gnarly calc() situation by --guess08, but at first glance this seems like it might be a worthwhile solution. It doesn’t give us exactly proportionate surface areas but it gets very close. It only starts to fall apart with super skyscraper-y and letterbox-y images.

A few quick notes regarding why this is written as it is and ways that it could be tweaked:

I capped the number of guesses at eight because any more seemed to just fail, Chrome and Safari wouldn’t interpret such a big calc() equation.

I set the default width and height to zero so that there is no max width restriction if the image tag is missing the width and height CSS variables. This could be changed, as could the ideal area variable (increase to get larger images, decrease to get smaller).

The 1px value in the max-width calculation is required so that the value is interpreted as a unit. That said, it doesn’t have to be pixels! Could change this to another unit like 1em or 1%.

If I wanted to display these evenly centred, I’d probably give the images some margin and then wrap them in a container with styles as below:

.container {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

Could also use CSS grid for a more consistent spacing result.

And a final reminder: this only visually scales the images. Try to avoid loading a 3000px wide image if you’re going to be displaying it around 200px wide, your users and the planet will thank you.

A CSS-only solution that might work in the future

So apparently more complex math functions including sqrt() might be coming to CSS in the future! See this issue raised by Lea Verou in the CSS specifications editor’s drafts repo and the exponential functions section from CSS Values and Units Module Level 4 in the W3C editor’s draft from 3 February 2020 (a couple days ago!).

I’m not sure when this would become part of the spec and no idea if / when the browsers might implement them, but here is a snippet that should work with that new function in theory:

img {
  --width: 0;
  --height: 0;
  --ideal-area: 40000;
  max-width: calc(var(--width) * sqrt(calc(var(--ideal-area) / calc(var(--width) * var(--height)))) / 2 * 1px);
}

I’m happy that Nick asked the question on Twitter, I actually need this on an upcoming project where I’ve only got Twig to work with which doesn’t support square roots, and I’d prefer to avoid JavaScript in this instance. Hopefully this is the solution, will update here if so.

A picture says a thousand words, so see Nick’s very nice demo of area-based image sizing with CSS to check out one possible outcome.


Edit 05.02.20

Edited a few words for clarity, added Nick’s demo, added future example incorporating a CSS-based sqrt() function.

Thanks Sam Baldwin for bringing future math function support in CSS to my attention!

Edit 06.02.20

Simplified the PHP + HTML example.

The original PHP + HTML example used a --max-width CSS variable instead of just applying a max-width directly. I used a CSS variable because I thought that they were compliant with a strict Content Security Policy that includes the style-src directive set to unsafe-inline. That assumption was incorrect, though there does seem to be some discussion about the topic.

Thanks Lizzie Malcolm for questioning the CSS var usage in the PHP example!

Edit 18.02.20

Changed parenthesis-enclosed arithmetic so that each is enclosed in calc(). Plain CSS seems to interpret arithmetic enclosed in parenthesis just fine, but SASS doesn’t seem to like it.

Published

The youngest lifeboat crew rescue airmen

A story from the past via the Whitby Lifeboat Museum.

The museum is at the end of Pier Road just past the arcades in the old Whitby lifeboat station. The station was one of the first, established in 1802. It was taken over by the Royal National Lifeboat Institution (RNLI) after the 1861 lifeboat disaster which resulted in the tragic loss of 12 from a crew of 13. The station was closed and turned in to a museum in 1957 and was temporarily brought back in to service in 2005 while Whitby’s existing station was demolished and rebuilt.

Donate to the RNLI.

YOUNGEST EVER LIFEBOAT CREW RESCUE AIRMEN

25th March 1942

At 10.55 am, a telephone message was sent to the lifeboat station from H.M. Coastguard stating that an aircraft was down in the sea 1 mile off Sandsend and that the lifeboat was 
requested to launch immediately.

The Motor Mechanic Jim Philpott realised that all of the regular crew were at sea fishing, and after the maroons were fired, he subsequently managed to find a retired Coxswain Thomas Welham (71), and mustered 5 more crew who were all just 16 years old.

The lifeboat proceeded to sea in patchy fog and soon found traces of oil on the water. Eventually, after a short search, the lifeboat located a rubber dinghy with four aircrew inside.

The airmen were taken aboard the lifeboat suffering from head wounds and one a leg injury.

All were landed at the fish quay about 30 minutes later and transferred to Whitby Hospital.

The Aircraft was a Lockheed Hudson of the R.A.F. which had earlier sunk.



The photograph below shows, left to right:

F. Russell, P. Storr, T. Lewis, Cox T. Welham, R. Russell, J. Philpott

A photo of R. Murfield is not available.

Each man (and boy) received 19 shillings (95p) for their services.



The Pilot expressed gratitude to the lifeboat crew, and also his surprise at the speed of their rescue.

Young Whitby lifeboat crewmen

Published

Notes from MozFest 2019

This is super delayed! I typed up my rough notes right after MozFest finished in October but never pressed publish. Voila.

MozFest is 10 years old! This was their last year at Ravensbourne in London. Sad, but I’m excited to see where it heads next.

This is a haphazard brain-dump of everything I want to remember and follow up on, a lot of questions for future consideration and resources that I need to explore. See also Common Knowledge’s notes from MozFest written by Gemma Copeland.

Read more

Published

Everything we had for Thanksgiving

This is everything we had for Thanksgiving last night, a spread of things that worked for our range of dietary requirements. I really loved everything we made and would happily have any of it again. See the menu below with recipes (typed out if mine, linked if not) and ingredient lists.

V = Vegetarian
Ve = Vegan
LF = Lactose Free
GF = Gluten Free
LowF = Low-FODMAP

An asterisk means that the dish isn’t 100% appropriate for that particular diet but could be suitable with an easy substitute.


Rosemary pecan pie – V, LF*, GF

Gluten free flour, vegetable oil, sugar, salt, milk / mylk, dark brown sugar, golden syrup, butter, vanilla extract, rosemary, eggs, pecans

Most pecan pie recipes call for corn syrup. That’s a bummer in and of itself, but it also makes it super difficult to make in the UK since it is really hard to find. Enter Ruby Tandoh’s rosemary pecan pie recipe which calls for golden syrup, an easy-to-find ingredient on British grocery store shelves. I really like the rosemary flavour in this, but I wouldn’t recommend steeping it too long and wouldn’t add the fresh rosemary needles on top. The flavour is just too strong.

I’ve noticed this before but always forget: this pie gets very dark on top, verging on burnt-looking. I think this may have to do with both the dark brown sugar and the cooking temperature / time. Next time, I may try BBC Good Food’s New England pecan pie recipe (also without corn syrup) or perhaps a combination of the two.

To make this gluten free, I used my grandma’s press-in pie crust recipe with gluten free flour. It worked well, but something about it could have been a bit better… I think that there could have been a little bit less fat, and maybe melted butter would have a better flavour. It’s also useful to mix this up in a bowl first, doing it in the plate is just a bit messy. I think that the brushed beaten egg actually does a lot to help the crust to stay together.

Spiced roasted carrots – V, Ve, LowF, LF, GF

Carrots, brown sugar, olive oil, coriander seeds, cumin seeds, fresh thyme

This is always one of my favourites. See the honey roasted carrots recipe from this Guardian article, adapted from a recipe by Ottolenghi. I always use brown sugar instead of honey, usually add a bit more cumin than it calls for, and sometimes add cayenne. It’s best to put the roasting tray towards the top of the oven and give the carrots a lot of space, otherwise they steam instead of roast.

You can cook this a lot earlier in the day and then just warm it through later on, or serve it room temperature.

Miso-glazed spicy brussels sprouts – V, Ve*, LF, GF

Brussels sprouts, salt, pepper, olive oil, honey, miso paste, rice vinegar, sesame oil

Loosely based on this Bon Appetit recipe. The sprouts were pretty small so I roasted them whole. This worked great, less prep. The “bottom of the oven” technique worked well to get them cooked all the way through, and then I brought them to the top of the oven to get a slightly better roasted texture. It’s also helpful because they can just sit down there under other things that need the more direct upper-oven heat.

I roasted them earlier in the day when I had more oven space for a big sheet tray (they need space so that they don’t steam). After that, I transferred them to a much smaller tray. When we got close to eating, I prepared the glaze by combining the ingredients to taste and heating it in a little pan, then poured it over the sprouts and popped them back in the oven to warm through.

Warm kale and wild rice salad – V, Ve, LowF, LF, GF

Wild rice, vegetable stock, kale, persimmons, squash, seeds, oranges, maple syrup, coriander seed, cumin seed

This was very loosely based on a Roasted Squash and Kale Salad recipe from Serious Eats. Our version had massaged kale, red and wild rice cooked in vegetable stock, sliced persimmons, roasted squash, toasted sunflower and pumpkin seeds, and a spiced maple and orange vinaigrette. Next time, I think I would roast the kale with the squash as suggested in the Serious Eats recipe, would possibly add some orange segments, and would probably go heavier with the spices in the dressing.

This is easy to prepare ahead or earlier in the day.

Old fashioned cornbread – V, LowF, LF*, GF

Coarse cornmeal / polenta, baking soda, baking powder, egg, butter, vinegar, milk / mylk, honey

I like a less cake-y cornbread, something that is just barely sweet. I like to put an absolute ton of honey or maple butter on top, and semi-savory, gritty cornbread contrasts that really well.

This recipe worked perfectly. I used the coarsest polenta I could find and used a faux milk-free “buttermilk” (1.5 T vinegar + Oatly Barista). It was some of the best cornbread I’ve had in a long time with a pleasant, slightly gritty texture. Not at all like the brick I made last year! My cast iron skillet was slightly too big though so the result was a little thin. Next time, it’d be better to increase the quantity or find a smaller skillet. I’d probably preheat the skillet in the oven as well to get a firmer crust on the bottom. The 20 minute cook time was perfect.

Cranberry relish – V, Ve, LowF, LF, GF

Frozen cranberries, orange, sugar

Was able to find frozen cranberries this year so we made my favourite cold cranberry relish, see mom’s notes for recipe.

This can be made way ahead and is very quick.

Roasted potatoes – V, Ve, LowF, LF, GF

Good roasting potatoes, rapeseed oil, salt, chopped rosemary, chopped thyme

Preheat the oven to 200C (400F) and position a rack towards the top third of your oven. Pour a good amount of rapeseed oil in to one or more roasting tins that are large enough to handle all of your potatoes with a bit of wiggle room, and then put this in the oven to preheat. Cut a bunch of good roasting potatoes in to very large chunks, then parboil in heavily salted water. Keep an eye on them; they’ll go to mush in the next step if they’re too soft! When they’re ready drain the water, then bash them around in the pot a little bit so that they get roughed up. When the oven has come to temperature, pull the tray(s) out of the oven and carefully dump in the potatoes. Coat with the hot oil, and season well with salt. Place in the upper third of your oven and roast for approximately 25-30 minutes. At this point, pull out the potatoes, shuffle them around, top them with chopped rosemary and thyme, and then put them back in the oven to roast for a further 10–15 minutes.

I did these at 180C since that’s the temperature that the nut roast called for. They maybe weren’t quite as all-over crispy as they would have been at a higher heat and they took a little longer, but they were still great.

Onion gravy – V, Ve*, LF*, GF

Onions, vegetable stock, gluten free flour, butter, chopped herbs, wine, salt, pepper

Slice 1-2 onions and cook gently over medium-low heat in a heavy pot until very soft and brown, approximately 30-45 minutes. Add a glug of white or red wine and simmer for a bit until the alcohol has burned off. Scrape any brown bits stuck to the pan in to the mixture. Add some vegetable stock, then bring the whole thing to a simmer. In a small bowl, make a beurre manié by mixing some flour and butter together. Add this to the gravy gradually to thicken it. Add the chopped herbs and cook until they have softened somewhat, then taste and balance out the flavours with salt, pepper, or additional wine.

Normally I make gravy from the chicken / turkey pan drippings but honestly… I might just stick to this in the future. It was *really* good, it’s veggie-friendly, and it means you don’t have to worry about making gravy right when everything is ready to go on the table. The pan drippings can go in to the stock later on instead.

Nut roast – V, GF*

Parsnips, savoy cabbage, hazelnuts, butter, onion, chestnut mushrooms, cooked chestnuts, stilton, brown breadcrumbs, fresh sage, egg, salt, pepper

We used Felicity Cloake’s perfect nut roast recipe. This is a very nutty, mushroom-y mixture wrapped in cabbage leaves, very presentable. It tastes a lot like stuffing, which is excellent in my book.

To make this gluten free, we used GF oat cakes for the “breadcrumbs”. We accidentally forgot the egg. The mixture didn’t slice *quite* as well because of this, but honestly it still held together pretty decently! We also maybe didn’t put quite enough salt and pepper in. This is a substantial mixture, so it’s worth remembering that it can take a lot of seasoning.

You might be able to make this vegan and/or lactose free. You could swap the butter for olive oil and could get away with omitting the egg. I’m not quite sure what you would do to replace the stilton though… It adds a creamy funkiness. Maybe some finely-chopped capers or green olives would be welcome, for the tanginess? Some chunks of tofu for the creaminess, marinated in a bit of Worcester sauce and / or dijon mustard? Not sure!

Spatchcock roasted chicken – LowF, LF*, GF

Whole chicken, butter or olive oil, fresh herbs on their stalks, salt, pepper

Made a chicken this year b/c most of us were either vegetarians or not big meat eaters!

To spatchcock the chicken, cut the backbone out and score the breastbone on the inside, then place it skin-up on to a rimmed baking sheet and press to break through the breastbone, making it nice and flat. Reserve the backbone for stock.

If possible, dry brine the chicken 24 hours ahead of time. Rub approximately 2.5 t salt and any other herbs / spices you want (lemon zest, pepper, sage, etc.) all over the chicken and then let sit in your fridge *uncovered*. This will draw the seasoning in to the meat and will allow the outside to dry out thoroughly, meaning crispier skin. The skin will start to look mottled and a little weird, and that’s ok!

About an hour before you’re planning to roast the chicken, remove it from the fridge and let it come to room temperature. Preheat the oven to approximately 180C (350F). Rub a good amount of butter or olive oil over the chicken and season it with just a little bit more flaky sea salt. Place your whole herbs underneath the chicken. Stick the probe of a digital oven thermometer deep in to the breast meat, being careful not to touch the bone. Place in the middle of the oven and roast until the thermometer temperature reaches 75C (170F). In my experience, this usually takes about an hour. Throw the backbone on the tray about halfway through if you’re going to use it for stock. If the skin doesn’t look crispy enough when the meat is done, move it towards the top of the oven and crank up the grill / broiler for a bit. Remove the thermometer before you do this, the direct heat can be a bit too much for it.

When the chicken is done, remove it from the oven and let rest at least 15 minutes before cutting in to it. The resting time is a good time to make a gravy or to finish off part-roasted potatoes in the oven.

Today (the day after), all of the chicken bones went in to a big pot with a whole bunch of peppercorns, bay leaves, onions with their skin, scrubbed and unpeeled carrots, and a lot of water. That has simmered for 4–5 hours and is now cooling. I’ll let it set in the fridge overnight and then will remove the fat from the top, reserving it to fry onions and spices for a curry later this week.

BTW the spatchcock technique should work well for a turkey too, but it will take longer in the oven and should rest longer as well.


Edit 7 December 2019 — I would definitely consider this squash, winter herb, and butterbean pie recipe (V, Ve*, GF*) by Anna Jones or this Chinese turnip cake recipe (V, Ve, LF, GF*) from Ottolenghi as well.

This year, I celebrated with some close girlfriends. It was my ninth Thanksgiving in the UK. Our kitchen isn’t the biggest, so we had a little candle-lit picnic in the front room on a stripy blanket my mom gave me years ago, the one on the bed in this pic.

Published

“A leaf a gourd a shell a net a bag a sling a sack a bottle a pot a box a container”

That’s right, they said. What you are is a woman. Possibly not human at all, certainly defective. Now be quiet while we go on telling the Story of the Ascent of Man the Hero.

Go on, say I, wandering off towards the wild oats, with Oo Oo in the sling and little Oom carrying the basket. You just go on telling how the mammoth fell on Boob and how Cain fell on Abel and how the bomb fell on Nagasaki and how the burning jelly fell on the villagers and how the missiles will fall on the Evil Empire, and all the other steps in the Ascent of Man.

If it is a human thing to do to put something you want, because it’s useful, edible or beautiful, into a bag, or a basket, or a bit of rolled bark or leaf, or a net woven of your own hair, or what have you, and then take it home with you, home being another, larger kind of pouch or bag a container for people, and then later on you take it out and eat it or share it or store it up for winter in a solider container or put it in the medicine bundle or the shrine or the museum, the holy place, the area that contains what is sacred, and the next day you probably do much the same again — if to do that is human, if that’s what it takes, then I am a human being after all. Fully, freely, gladly, for the first time.

From Ursula K. Le Guin’s essay The Carrier Bag Theory of Fiction published by Ignota

GC gave me this book the other day, perfectly timed.

It can feel like the path to success, whatever on earth success actually is, takes some sort of aggro-ambition. What if it is gentler, more of a methodical and deliberate accumulation than a conquest?

SB has been playing Death Stranding and I’ve really enjoyed following along. The arc is definitely hero-centric, and of course the story is way out there in sci-fi land, but the main mechanic of accepting and delivering cargo is much more human than so many other supposedly more realistic video games.

I’d like to get and read Elizabeth Fisher’s Women’s Creation from 1975, but it might be tough to find in print. Thankfully the Internet Archive seems to offer it for borrowing. Pretty cool, I didn’t know that they had a lending library for scanned books.

Published

“5:30am — wake up and lie there and think”

  • 5:30am — wake up and lie there and think
  • 6:15am — get up and eat breakfast (lots)
  • 7:15am — get to work writing, writing, writing
  • Noon — lunch
  • 1–3pm — reading, music
  • 3–5pm — correspondence, maybe house cleaning
  • 5–8pm — make dinner and eat it
  • After 8pm — I tend to be very stupid and we won’t talk about this

Ursula K. Le Guin’s daily routine

Via GC who I think may have come across it via this tweet. Can be found in the book Ursula Le Guin: The Last Interview.

Published

Notes from Redecentralize 2019

Been a busy few days with Redecentralize on Friday followed by MozFest over the weekend. Redecentralize was a one-day unconference at 4th Floor Studios in Whitechapel. The event was expertly organised by Ira Bolychevsky and her crack team.

It was a day of thought-provoking conversations and notebook scribbling. This is an attempt to decode the scribbles, make some follow-up plans, and to generally summarise the day from my perspective. There was a lot going on so I can’t cover it all, but I’m going to keep an eye out for other people’s notes via the Redecentralize newsletter.

\              \                      \                   \
\\\   \   \    \\            \        \\       \       \  \\
\\\\\ \\\ \\\  \\\   \    \  \\     \ \\\  \   \\  \   \\ \\\ \
\\ \\\\\\\\\\\ \\\\ \\\\  \\\\\\   \\\\\\\\\\\ \\\ \\\ \\\\\\\\
\\   \\\  \\\\\\\ \\\\\\\\\\\\\\\\\\\\\\ \\ \\\\\\\\\\\\\\\\  \
 \     \    \\  \   \    \\\  \  \\\   \  \   \\\ \\\ \\\  \   
              \            \       \            \   \   \

Read more

Published

Ikigai 生き甲斐

Ikigai 生き甲斐 = the ideal reason to get up in the morning. There are web articles aplenty on this topic but it doesn’t need much explaining, it’s just a more structured way of thinking about things you already have knocking around in your head. Worth keeping in mind.

                         . . . . . . .
                    .                     .
                 .         FOR               .
               .                LOVE           .
              .                                 .
             . . . . . . .           . . . . . . .
          . .               .     .               . .
      .     .   PA            . .        MI       .     .
   .        .    SS        .       .      SS      .        .
  .         .     IO      .         .      IO     .         .
 .           .      N    . . . . . . .       N   .  FOR      .
.   FOR       .     .   .             .   .     .             .
.              . .      .             .      . .    CO        .
.   TA         . .      .      🌱     .      . .     MM       .
.    LE       .     .   .             .   .     .     UN      .
 .    NT     .  PR       . . . . . . .           .     IT    .
  .         .    OF       .         .   VO        .      Y  .
   .        .     ES       .       .     CA       .        .
      .     .      SI         . .         TI      .     .
          . .       ON      .     .        ON     . .
             . . . . . . .           . . . . . . .
              .                                 .
               .          FOR                  .
                 .             MONEY         .
                    .                     .
                         . . . . . . .