Skip to content

Instantly share code, notes, and snippets.

@ianobermiller
Created July 12, 2021 00:47
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ianobermiller/9f17f1022bc75c2228d742d97fe1f82d to your computer and use it in GitHub Desktop.
Save ianobermiller/9f17f1022bc75c2228d742d97fe1f82d to your computer and use it in GitHub Desktop.
WiFi QR Code, single HTML file
<!DOCTYPE html>
<html>
<head>
<title>WiFi Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- https://news.ycombinator.com/item?id=26923316 -->
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>&#128272;</text></svg>">
<style>
body, textarea {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 16px;
}
main {
margin: 0 auto;
max-width: 400px;
text-align: center;
}
canvas {
height: 200px;
width: 200px;
}
label {
display: block;
margin: 12px;
text-align: left;
}
textarea {
box-sizing: border-box;
font-family: Menlo, Consola, monospace;
font-weight: bold;
margin-top: 4px;
padding: 8px;
width: 100%;
}
textarea:invalid {
border: 2px dashed red;
}
/* https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ */
@supports (display: grid) {
.input {
/* easy way to plop the elements on top of each other and have them both sized based on the tallest one's height */
display: grid;
}
.input::after {
/* Note the weird space! Needed to preventy jumpy behavior */
content: attr(data-replicated-value) " ";
/* This is how textarea text behaves */
white-space: pre-wrap;
/* Hidden from view, clicks, and screen readers */
visibility: hidden;
}
.input > textarea {
/* You could leave this, but after a user resizes, then it ruins the auto sizing */
resize: none;
/* Firefox shows scrollbar on growth, you can hide like this. */
overflow: hidden;
}
.input > textarea,
.input::after {
/* Identical styling required!! */
border: 1px solid black;
border-radius: 4px;
box-sizing: border-box;
margin-top: 4px;
padding: 8px;
font: inherit;
/* Place on top of each other */
grid-area: 1 / 1 / 2 / 2;
}
}
</style>
</head>
<body>
<main>
<h1>WiFi Login</h1>
<p>Scan with your phone to join automatically!</p>
<canvas id="qr"></canvas>
<label>
Network Name
<div class="input">
<textarea
id="ssid"
type="text"
rows="1"
autocapitalize="off"
autocorrect="off"
autofocus="true"></textarea>
</div>
</label>
<label>
Password
<div class="input">
<textarea
id="password"
type="text"
minlength="8"
rows="1"
autocapitalize="off"
autocorrect="off"
title="Must be at least 8 characters."></textarea>
</div>
</label>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
<script>
(function() {
var qr = new QRious({
element: document.getElementById('qr'),
value: 'https://github.com/neocotic/qrious',
size: 400 // twice the size for extra crispiness
});
var ssidInput = document.getElementById('ssid');
var passwordInput = document.getElementById('password');
// https://github.com/bndw/wifi-card/blob/master/src/components/Card.js
var needsEscape = {'"': true, ';': true, ',': true, ':': true, '\\': true};
function escape(v) {
return v.split('').map(c => needsEscape[c] ? '\\' + c : c).join('');
}
function updateQRCode() {
this.parentNode.dataset.replicatedValue = this.value;
var ssid = escape(ssidInput.value);
var password = escape(passwordInput.value);
qr.value = 'WIFI:T:WPA;S:' + ssid + ';P:' + password + ';;';
}
ssidInput.addEventListener('input', updateQRCode);
passwordInput.addEventListener('input', updateQRCode);
})();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment