The HTML page is built using a grunt workflow (node.js), combining CSS, JavaScript, SVG, PNGs and MP3s into a single file, which is then gzipped to half its size.
To reduce the number of http requests required to serve the page to 1x, the CSS, JavaScript and SVGs are minified and included inline. The PNG favicon and MP3s are base64 encoded and also included inline.
Each asset is minified as much as possible to ensure the resulting HTML will fit on the device.
After gzipping, the result is a single 8kb file, which is served by the MicroPython web server.
All of the graphics aside from the favicon are SVG for responsive scaling on all devices, phones through to 27" desktops.
Button interactivity is powered by JavaScript which uses CSS3 transformations for animating the SVGs. When the button is clicked, JavaScript plays the MP3s and executes http requests to the MicroPython server which triggers the relay.
I am using a WeMos D1 Mini, but any ESP8266 board running the latest MicroPython firmware will do. Simply launch a WebREPL session to upload the files, then reboot to configure the AP and web server.
The captive portal is not working yet. So, clients connecting to the unencrypted access point will have to visit the access point IP to see the HTML. Once I get it working, all DNS traffic will be spoofed and all traffic redirected to the HTML page.
My parts are now included in Fritzing.
https://github.com/mcauser/Fritzing-Part-WeMos-D1-Mini
https://github.com/mcauser/Fritzing-Part-WeMos-D1-mini-Shields