Clever Bits of Awesome Mud Works

Awesome Mud Works is all about pottery classes and displaying some cool ceramics. I decided to build this site as a project that I could further research docker tools. Although it is just a simple site with a few pages, it does do a few clever things that I would like to highlight.

This site was also designed by me. Designing websites is harder then it looks.

Intrinsic Ratio with CSS Custom Properties

I'll start off with something a little easier to explain before I get into the weeds. This one actually didn't take long at all to put together and it worked so well that I was just simply proud of it.

The problem that I had was the common problem of embedding a map into a web page. The iframe is a set width and height, but I needed it to resize if the browser width was smaller. I also wanted it to take up the full width if the browser width was larger then the map. Like I said, this is a simple common problem that has been fixed before using a technique called intrinsic ratio.
.intrinsic-ratio-4-3 {
  position: relative;
  padding-bottom: 75%;
  height: 0;
  overflow: hidden;

.intrinsic-ratio-4-3 > iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

Oh, but we have CSS custom properties now! I can just use inline style to set these and now there is no more need to set ratio modifiers.
.u-intrinsicRatio {
  --intrinsicRatio-width: 4;
  --intrinsicRatio-height: 3;
  position: relative;
  padding-bottom: calc(100% * (var(--intrinsicRatio-height) / var(--intrinsicRatio-width)));
  height: 0;
  overflow: hidden;

.u-intrinsicRatio > iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

An example of the HTML to embed a map into the page. Note that I'm using the inline style to set the two custom properties.
<div class="u-intrinsicRatio" style="--intrinsicRatio-width: 600; --intrinsicRatio-height: 450;">
<iframe src="!1m18!1m12!1m3!1d11105.781763217377!2d-111.77621831145693!3d40.70896121377259!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x875266cc99e2a0d3%3A0x318ecd5a5bbd8d08!2sGrandeur+Peak!5e1!3m2!1sen!2sus!4v1523276592328" width="600" height="450" frameborder="0" style="border:0" allowfullscreen></iframe>

Done and I made a gist. Now the google map here will simply resize and keep the desired ratio without need to update the CSS. I just felt so clever after doing that.

Contact Details with Recaptcha and a Twist

Okay, next up is the common contact us page. This one actually started off with being a standard contact form protected by a recaptcha challenge. Don't want a bunch of robots filling out contact forms with junk, right? Oh, contact forms are so ... well, not for humans. Why would a small business (really, just one person) want to have potential clients filtered through a contact form?

My thought is that you want to develop that one on one communication quickly. Having the preferred way of contact to be filling out a form, with say your email address, raises a question of what happens with your email address after you hit submit. Does it get stored in some database and then used to send out unsolicited email newsletters? Who knows?

But, we still want some protection from robots, right? Can't just be putting the email and phone number on a website like this and expect the owner of it not to get put on all kinds of junk email lists and such (no guarantees on that of course). But, for the humans out there, they need a simple way of contacting the business. So, I still had the Recaptcha stuff working, I just needed to tweak the page into displaying the contact details after the recaptcha was solved.

Ah, look, the recaptcha can actually be done invisibly now in the background. Google already knows who is a robot and who is human for the most part (they are clever like that). So now when you load the page it checks to see if you pass the test and inserts the contact details into the page on success. Hooray!

Oh, but why check again if you are a robot if you revisit that page? That would be silly, right. Time to use some local storage for the win. So, I'll just keep a checksum of the contact details content in local storage and compare that. If they still match then the user can see the contact details without the need to verify their anti-robotoness. When the contact details change the checksum will no longer match and then they would need to reverify, no problem.

I saw this one as pretty useful and figured I could use it in other projects. I pulled out the bits into a git repo here as Anti-robot Snippet.

A slightly Over Engineered Approach to an Image Gallery

CSS grid is cool and I wanted to use it in a cool way. I have a folder of pottery pictures like mugs, plates, bowls, and all that. I also have convert and make on my machine (who doesn't, right). What happens when you combine these things? You get a gallery page that shows 6 pictures of some handmade ceramics.

6x4 layout
4x6 layout
3x8 layout

Oh, but look at this bit of Makefile madness that got a little too clever.
# Resizes jpgs that have a hint ratio in their name to multiple sizes.
# For example the cat-2-3.jpg will create resized jpgs:
# cat.280x420.jpg, cat.600x900.jpg, cat.1200x1800.jpg
unit_sizes := 140 300 600

# Find all hinted jpgs in gallery/ that have the ratio hint suffix (-2-2.jpg)
hinted := $(shell find media/gallery -name '*-[0-9]-[0-9].jpg')

# Init with nothing and allow each RESIZE_TARGET_template to append to this var.
resized :=

# clear out any suffixes


.PHONY : all clean

all : $$(resized)

define RESIZE_TARGET_template
resized += $$(patsubst %-$(1).jpg,%.$(2).jpg,$$(filter %-$(1).jpg,$$(hinted)))
%.$(2).jpg : %-$(1).jpg
	convert $$< -resize $(2) $$@

define UNIT_template
$$(eval $$(call RESIZE_TARGET_template,1-1,$$(strip $$(shell echo "1*$(1)" | bc))x$$(strip $$(shell echo "1*$(1)" | bc))))
$$(eval $$(call RESIZE_TARGET_template,1-2,$$(strip $$(shell echo "1*$(1)" | bc))x$$(strip $$(shell echo "2*$(1)" | bc))))
$$(eval $$(call RESIZE_TARGET_template,1-3,$$(strip $$(shell echo "1*$(1)" | bc))x$$(strip $$(shell echo "3*$(1)" | bc))))

$$(eval $$(call RESIZE_TARGET_template,2-1,$$(strip $$(shell echo "2*$(1)" | bc))x$$(strip $$(shell echo "1*$(1)" | bc))))
$$(eval $$(call RESIZE_TARGET_template,2-2,$$(strip $$(shell echo "2*$(1)" | bc))x$$(strip $$(shell echo "2*$(1)" | bc))))
$$(eval $$(call RESIZE_TARGET_template,2-3,$$(strip $$(shell echo "2*$(1)" | bc))x$$(strip $$(shell echo "3*$(1)" | bc))))

$$(eval $$(call RESIZE_TARGET_template,3-1,$$(strip $$(shell echo "3*$(1)" | bc))x$$(strip $$(shell echo "1*$(1)" | bc))))
$$(eval $$(call RESIZE_TARGET_template,3-2,$$(strip $$(shell echo "3*$(1)" | bc))x$$(strip $$(shell echo "2*$(1)" | bc))))
$$(eval $$(call RESIZE_TARGET_template,3-3,$$(strip $$(shell echo "3*$(1)" | bc))x$$(strip $$(shell echo "3*$(1)" | bc))))

$(foreach size,$(unit_sizes),$(eval $(call UNIT_template,$(size))))

clean :
	@echo "Removing resized files"
	@echo $(resized) | xargs rm
I made a Gist of the

At least I kept it less clever on the cropping and resizing from the source images. I manually set each cropped and resized image like this. Brings new meaning to Lovingly hand-crafted website.
media/gallery/test/FullSizeRender-3-2.jpg : source-media/artist/neale/gallery/FullSizeRender.jpg
	convert $< -format jpg -crop 1635x1090+411+582 +repage $@;

Now for the automatic layout depending on browser width (responsive design, ya know). The design constraint? I basically keep the layouts to have a total of 24 cells and all 24 cells need to be filled. For my six pictures I set three of them to be in a 3:2 ratio and the other three are 1:2. That way they fill all 24 cells when laid out in a 6x4 layout, 4x6 layout, and 3x8 layouts. I also simply stack them when viewing at even smaller widths.

I think going over detail of the CSS for the gallery grid would be out of scope for this article. If you are curious; check out the gallery on Awesome Mud Works with your browser dev tools.

Using docker for the first time in a web project

I'll confess; near the end of publishing this project I finally understood when to use a volume versus a bind-mount in docker. It does help to actually read the documentation closely instead of being surprised or confused why something doesn't work.

I split up my project into having a few containers. I use a NGINX container with a production and separate development config. A chill container that creates a static site in production or database driven one in development. For the fancy contact details page, I have an api container that is a python app. And then a container that runs awstats since I thought that would be cool.

Still trying some things out with this setup, but I think it works well enough. The deployment process was a bit different then I was use to, but it worked.

I created a seed project of my docker setup that is based off of this site. The cookiecutter project is on GitHub as cookiecutter-chill.