├── .gitattributes ├── .gitignore ├── README.md ├── requirements.txt ├── server.py ├── static ├── main.js ├── styles.css └── water.css └── templates └── index.html /.gitattributes: -------------------------------------------------------------------------------- 1 | *.css linguist-detectable=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | venv/ 3 | codes/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QR-Code generator with web UI 2 | 3 | This is a QR-generator service you can host on your own machine. 4 | 5 | Basically its a [Flask server](https://www.palletsprojects.com/p/flask/) wrapped around this [QR code image generator](https://pypi.org/project/qrcode/) providing an easy-to-use web UI (and an API) to generate QR-codes. 6 | 7 | *Hint: QR-Codes are cached in folder `codes`. In production you should deploy a proper clean-up mechanism here ;)* 8 | 9 | ## Frontend 10 | 11 | ![Screenshot_2023-10-24_09-08-15](https://github.com/mbarde/flask-qrcode/assets/4497578/4ed4fbda-831f-4a6c-8fc9-d674b3c5d603) 12 | 13 | ## API 14 | 15 | Example: `http://localhost:5000?url=https://mbarde.de&boxsize=50` 16 | 17 | Expected parameters: 18 | 19 | * `url`: URL to "qr-encode" 20 | * `size` (optional): Size (width & height) of resulting QR code image 21 | * `boxsize` (optional): Size of single box of the QR code (when `size` is set as well, this is the size of each box *before* image gets resized) 22 | * `fill` (optional): Hex code for fill color (without `#`) 23 | * `back` (optional): Hex code for background color (without `#`) 24 | 25 | ## Setup & run 26 | 27 | Within your [virtual environment](https://docs.python.org/3/tutorial/venv.html): 28 | ``` 29 | pip install -r requirements.txt 30 | env FLASK_APP=server.py flask run --host=0.0.0.0 31 | ``` 32 | 33 | Access via http://localhost:5000/ 34 | 35 | ## Serve with `waitress` 36 | 37 | In production enviroments I suggest serving the application via [waitress](https://flask.palletsprojects.com/en/2.3.x/deploying/waitress/): 38 | 39 | ``` 40 | pip install waitress 41 | waitress-serve --port=5000 --call "server:create_app" 42 | ``` 43 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | qrcode[pil] 3 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flask import abort 3 | from flask import Flask 4 | from flask import render_template 5 | from flask import request 6 | from flask import send_file 7 | from flask import send_from_directory 8 | from os import mkdir 9 | from os import path 10 | from PIL.Image import LANCZOS 11 | 12 | import qrcode 13 | 14 | 15 | def create_app(): 16 | app = Flask(__name__) 17 | 18 | @app.route('/') 19 | @app.route('/gen') 20 | def index(): 21 | url = getURL() 22 | if url is False: 23 | return render_template( 24 | 'index.html', 25 | title='Create QR-Codes', 26 | ) 27 | boxSize = getIntegerParameter('boxsize', 10, 1, 100) 28 | size = getIntegerParameter('size', False, 10, 1000) 29 | 30 | fill = getHexParameter('fill', '000000') 31 | back = getHexParameter('back', 'FFFFFF') 32 | 33 | codesFolderName = 'codes' 34 | initCodesFolder(codesFolderName) 35 | filename = generateFilename( 36 | codesFolderName, url, size, boxSize, fill, back) 37 | 38 | if not path.exists(filename): 39 | img = generateQRCodeImage( 40 | url, size, boxSize, fill, back) 41 | img.save(filename) 42 | 43 | return send_file(filename) 44 | 45 | def getURL(): 46 | url = request.args.get('url', '') 47 | if len(url) == 0: 48 | return False 49 | return url 50 | 51 | def getIntegerParameter(key, default, min, max): 52 | abortMessage = 'Please specify a valid value for {0} (a number between {1} and {2})'.format( # noqa: E501 53 | key, min, max) 54 | value = request.args.get(key, default) 55 | if value is default: 56 | return value 57 | try: 58 | value = int(value) 59 | except ValueError: 60 | abort(400, abortMessage) 61 | if value > max: 62 | abort(400, abortMessage) 63 | if value < min: 64 | abort(400, abortMessage) 65 | return value 66 | 67 | def getHexParameter(key, default): 68 | value = request.args.get(key, default) 69 | if len(value) != 6: 70 | return default 71 | return value 72 | 73 | def initCodesFolder(folderName): 74 | if not path.exists(folderName): 75 | mkdir(folderName) 76 | 77 | def generateFilename(codesFolderName, url, size, boxSize, fill, back): 78 | filename = normalizeFilename(url) 79 | sizeStr = 'auto' 80 | if size is not False: 81 | sizeStr = str(size) 82 | fullFilename = '{0}/{1}.{2}.{3}.{4}.{5}.png'.format( 83 | codesFolderName, filename, boxSize, sizeStr, fill, back) 84 | return fullFilename 85 | 86 | def generateQRCodeImage(url, size, boxSize, fill, back): 87 | qr = qrcode.QRCode( 88 | version=None, 89 | error_correction=qrcode.constants.ERROR_CORRECT_L, 90 | box_size=boxSize, 91 | border=4, 92 | ) 93 | qr.add_data(url) 94 | qr.make(fit=True) 95 | img = qr.make_image( 96 | fill_color=hex2rgb(fill), 97 | back_color=hex2rgb(back), 98 | ) 99 | 100 | if size is not False: 101 | img = img.resize((size, size), LANCZOS) 102 | 103 | return img 104 | 105 | def normalizeFilename(s): 106 | # https://gist.github.com/seanh/93666 107 | import string 108 | valid_chars = '-_.() %s%s' % (string.ascii_letters, string.digits) 109 | filename = ''.join(c for c in s if c in valid_chars) 110 | filename = filename.replace(' ', '_') 111 | return filename 112 | 113 | def hex2rgb(hex): 114 | return tuple(int(hex[i:i+2], 16) for i in (0, 2, 4)) 115 | 116 | @app.route('/static/') 117 | def sendStaticResources(path): 118 | return send_from_directory('static', path) 119 | 120 | return app 121 | 122 | 123 | if __name__ == '__main__': 124 | app = create_app() 125 | app.run() 126 | -------------------------------------------------------------------------------- /static/main.js: -------------------------------------------------------------------------------- 1 | function createQRCode(event) { 2 | let url = document.getElementById('url').value; 3 | if (!url || url.length === 0) { 4 | document.getElementById('qrcode').src = ''; 5 | document.getElementById('qrcode').style.display = 'none'; 6 | document.getElementById('btn-download').style.display = 'none'; 7 | document.getElementById('btn-copy').style.display = 'none'; 8 | event.preventDefault(); 9 | return false; 10 | } 11 | 12 | if (url.endsWith('/')) url = url.slice(0, -1); 13 | let src = '?url=' + url; 14 | 15 | let boxsize = document.getElementById('boxsize').value; 16 | if (boxsize) { 17 | if (boxsize < 1) boxsize = 1; 18 | if (boxsize > 100) boxsize = 100; 19 | src += '&boxsize=' + boxsize; 20 | } 21 | 22 | const clrfill = document.getElementById('clrfill').value; 23 | if (clrfill) src += '&fill=' + clrfill.slice(1); 24 | 25 | const clrback = document.getElementById('clrback').value; 26 | if (clrback) src += '&back=' + clrback.slice(1); 27 | 28 | document.getElementById('qrcode').src = src; 29 | document.getElementById('qrcode').style.display = 'block'; 30 | 31 | const btnDownload = document.getElementById('btn-download'); 32 | btnDownload.href = src; 33 | btnDownload.style.display = 'block'; 34 | 35 | const btnCopy = document.getElementById('btn-copy'); 36 | btnCopy.onclick = () => { 37 | text2clip(window.location.href + src, btnCopy); 38 | } 39 | btnCopy.style.display = 'block'; 40 | 41 | event.preventDefault(); 42 | return false; 43 | } 44 | 45 | function text2clip(text, el) { 46 | let dummy = document.createElement('input'); 47 | dummy.value = text; 48 | document.body.appendChild(dummy); 49 | dummy.select(); 50 | dummy.setSelectionRange(0, 99999); /* mobile devices */ 51 | document.execCommand('copy'); 52 | dummy.remove(); 53 | if (el !== null) { 54 | el.classList.add('shake'); 55 | setTimeout(function() { 56 | el.classList.remove('shake'); 57 | }, 500); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /static/styles.css: -------------------------------------------------------------------------------- 1 | .main { 2 | column-gap: 50px; 3 | display: flex; 4 | flex-wrap: wrap; 5 | } 6 | 7 | #qrcode { 8 | border: 1px solid var(--border); 9 | display: none; 10 | padding: 10px; 11 | } 12 | 13 | form { 14 | max-width: 500px; 15 | } 16 | 17 | form label { 18 | display: block; 19 | font-size: 1.2em; 20 | font-weight: bold; 21 | margin-top: 2em; 22 | } 23 | 24 | form input { 25 | margin-bottom: 20px; 26 | margin-top: 10px; 27 | width: 100%; 28 | } 29 | 30 | form .colors { 31 | column-gap: 10px; 32 | display: flex; 33 | } 34 | 35 | form .colors > div { 36 | flex-basis: 50%; 37 | flex-grow: 1; 38 | flex-shrink: 1; 39 | } 40 | 41 | #btn-copy, 42 | #btn-download { 43 | display: none; 44 | float: left; 45 | margin-right: 10px; 46 | } 47 | 48 | /* animations */ 49 | .shake { 50 | animation: shake 0.5s; 51 | animation-iteration-count: infinite; 52 | } 53 | 54 | @keyframes shake { 55 | 0% { transform: translate(1px, 1px) rotate(0deg); } 56 | 10% { transform: translate(-1px, -2px) rotate(-1deg); } 57 | 20% { transform: translate(-3px, 0px) rotate(1deg); } 58 | 30% { transform: translate(3px, 2px) rotate(0deg); } 59 | 40% { transform: translate(1px, -1px) rotate(1deg); } 60 | 50% { transform: translate(-1px, 2px) rotate(-1deg); } 61 | 60% { transform: translate(-3px, 1px) rotate(0deg); } 62 | 70% { transform: translate(3px, 1px) rotate(-1deg); } 63 | 80% { transform: translate(-1px, -1px) rotate(1deg); } 64 | 90% { transform: translate(1px, 2px) rotate(0deg); } 65 | 100% { transform: translate(1px, -2px) rotate(-1deg); } 66 | } 67 | -------------------------------------------------------------------------------- /static/water.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Automatic version: 3 | * Uses light theme by default but switches to dark theme 4 | * if a system-wide theme preference is set on the user's device. 5 | */ 6 | 7 | :root { 8 | --background-body: #fff; 9 | --background: #efefef; 10 | --background-alt: #f7f7f7; 11 | --selection: #9e9e9e; 12 | --text-main: #363636; 13 | --text-bright: #000; 14 | --text-muted: #70777f; 15 | --links: #0076d1; 16 | --focus: #0096bfab; 17 | --border: #dbdbdb; 18 | --code: #000; 19 | --animation-duration: 0.1s; 20 | --button-base: #d0cfcf; 21 | --button-hover: #9b9b9b; 22 | --scrollbar-thumb: rgb(170, 170, 170); 23 | --scrollbar-thumb-hover: var(--button-hover); 24 | --form-placeholder: #949494; 25 | --form-text: #1d1d1d; 26 | --variable: #39a33c; 27 | --highlight: #ff0; 28 | --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); 29 | } 30 | 31 | @media (prefers-color-scheme: dark) { 32 | :root { 33 | --background-body: #202b38; 34 | --background: #161f27; 35 | --background-alt: #1a242f; 36 | --selection: #1c76c5; 37 | --text-main: #dbdbdb; 38 | --text-bright: #fff; 39 | --text-muted: #a9b1ba; 40 | --links: #41adff; 41 | --focus: #0096bfab; 42 | --border: #526980; 43 | --code: #ffbe85; 44 | --animation-duration: 0.1s; 45 | --button-base: #0c151c; 46 | --button-hover: #040a0f; 47 | --scrollbar-thumb: var(--button-hover); 48 | --scrollbar-thumb-hover: rgb(0, 0, 0); 49 | --form-placeholder: #a9a9a9; 50 | --form-text: #fff; 51 | --variable: #d941e2; 52 | --highlight: #efdb43; 53 | --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); 54 | } 55 | } 56 | 57 | html { 58 | scrollbar-color: rgb(170, 170, 170) #fff; 59 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 60 | scrollbar-width: thin; 61 | } 62 | 63 | @media (prefers-color-scheme: dark) { 64 | 65 | html { 66 | scrollbar-color: #040a0f #202b38; 67 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 68 | } 69 | } 70 | 71 | @media (prefers-color-scheme: dark) { 72 | 73 | html { 74 | scrollbar-color: #040a0f #202b38; 75 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 76 | } 77 | } 78 | 79 | @media (prefers-color-scheme: dark) { 80 | 81 | html { 82 | scrollbar-color: #040a0f #202b38; 83 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 84 | } 85 | } 86 | 87 | @media (prefers-color-scheme: dark) { 88 | 89 | html { 90 | scrollbar-color: #040a0f #202b38; 91 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 92 | } 93 | } 94 | 95 | @media (prefers-color-scheme: dark) { 96 | 97 | html { 98 | scrollbar-color: #040a0f #202b38; 99 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 100 | } 101 | } 102 | 103 | @media (prefers-color-scheme: dark) { 104 | 105 | html { 106 | scrollbar-color: #040a0f #202b38; 107 | scrollbar-color: var(--scrollbar-thumb) var(--background-body); 108 | } 109 | } 110 | 111 | body { 112 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Segoe UI Emoji', 'Apple Color Emoji', 'Noto Color Emoji', sans-serif; 113 | line-height: 1.4; 114 | max-width: 800px; 115 | margin: 20px auto; 116 | padding: 0 10px; 117 | word-wrap: break-word; 118 | color: #363636; 119 | color: var(--text-main); 120 | background: #fff; 121 | background: var(--background-body); 122 | text-rendering: optimizeLegibility; 123 | } 124 | 125 | @media (prefers-color-scheme: dark) { 126 | 127 | body { 128 | background: #202b38; 129 | background: var(--background-body); 130 | } 131 | } 132 | 133 | @media (prefers-color-scheme: dark) { 134 | 135 | body { 136 | color: #dbdbdb; 137 | color: var(--text-main); 138 | } 139 | } 140 | 141 | button { 142 | transition: 143 | background-color 0.1s linear, 144 | border-color 0.1s linear, 145 | color 0.1s linear, 146 | box-shadow 0.1s linear, 147 | transform 0.1s ease; 148 | transition: 149 | background-color var(--animation-duration) linear, 150 | border-color var(--animation-duration) linear, 151 | color var(--animation-duration) linear, 152 | box-shadow var(--animation-duration) linear, 153 | transform var(--animation-duration) ease; 154 | } 155 | 156 | @media (prefers-color-scheme: dark) { 157 | 158 | button { 159 | transition: 160 | background-color 0.1s linear, 161 | border-color 0.1s linear, 162 | color 0.1s linear, 163 | box-shadow 0.1s linear, 164 | transform 0.1s ease; 165 | transition: 166 | background-color var(--animation-duration) linear, 167 | border-color var(--animation-duration) linear, 168 | color var(--animation-duration) linear, 169 | box-shadow var(--animation-duration) linear, 170 | transform var(--animation-duration) ease; 171 | } 172 | } 173 | 174 | input { 175 | transition: 176 | background-color 0.1s linear, 177 | border-color 0.1s linear, 178 | color 0.1s linear, 179 | box-shadow 0.1s linear, 180 | transform 0.1s ease; 181 | transition: 182 | background-color var(--animation-duration) linear, 183 | border-color var(--animation-duration) linear, 184 | color var(--animation-duration) linear, 185 | box-shadow var(--animation-duration) linear, 186 | transform var(--animation-duration) ease; 187 | } 188 | 189 | @media (prefers-color-scheme: dark) { 190 | 191 | input { 192 | transition: 193 | background-color 0.1s linear, 194 | border-color 0.1s linear, 195 | color 0.1s linear, 196 | box-shadow 0.1s linear, 197 | transform 0.1s ease; 198 | transition: 199 | background-color var(--animation-duration) linear, 200 | border-color var(--animation-duration) linear, 201 | color var(--animation-duration) linear, 202 | box-shadow var(--animation-duration) linear, 203 | transform var(--animation-duration) ease; 204 | } 205 | } 206 | 207 | textarea { 208 | transition: 209 | background-color 0.1s linear, 210 | border-color 0.1s linear, 211 | color 0.1s linear, 212 | box-shadow 0.1s linear, 213 | transform 0.1s ease; 214 | transition: 215 | background-color var(--animation-duration) linear, 216 | border-color var(--animation-duration) linear, 217 | color var(--animation-duration) linear, 218 | box-shadow var(--animation-duration) linear, 219 | transform var(--animation-duration) ease; 220 | } 221 | 222 | @media (prefers-color-scheme: dark) { 223 | 224 | textarea { 225 | transition: 226 | background-color 0.1s linear, 227 | border-color 0.1s linear, 228 | color 0.1s linear, 229 | box-shadow 0.1s linear, 230 | transform 0.1s ease; 231 | transition: 232 | background-color var(--animation-duration) linear, 233 | border-color var(--animation-duration) linear, 234 | color var(--animation-duration) linear, 235 | box-shadow var(--animation-duration) linear, 236 | transform var(--animation-duration) ease; 237 | } 238 | } 239 | 240 | h1 { 241 | font-size: 2.2em; 242 | margin-top: 0; 243 | } 244 | 245 | h1, 246 | h2, 247 | h3, 248 | h4, 249 | h5, 250 | h6 { 251 | margin-bottom: 12px; 252 | margin-top: 24px; 253 | } 254 | 255 | h1 { 256 | color: #000; 257 | color: var(--text-bright); 258 | } 259 | 260 | @media (prefers-color-scheme: dark) { 261 | 262 | h1 { 263 | color: #fff; 264 | color: var(--text-bright); 265 | } 266 | } 267 | 268 | h2 { 269 | color: #000; 270 | color: var(--text-bright); 271 | } 272 | 273 | @media (prefers-color-scheme: dark) { 274 | 275 | h2 { 276 | color: #fff; 277 | color: var(--text-bright); 278 | } 279 | } 280 | 281 | h3 { 282 | color: #000; 283 | color: var(--text-bright); 284 | } 285 | 286 | @media (prefers-color-scheme: dark) { 287 | 288 | h3 { 289 | color: #fff; 290 | color: var(--text-bright); 291 | } 292 | } 293 | 294 | h4 { 295 | color: #000; 296 | color: var(--text-bright); 297 | } 298 | 299 | @media (prefers-color-scheme: dark) { 300 | 301 | h4 { 302 | color: #fff; 303 | color: var(--text-bright); 304 | } 305 | } 306 | 307 | h5 { 308 | color: #000; 309 | color: var(--text-bright); 310 | } 311 | 312 | @media (prefers-color-scheme: dark) { 313 | 314 | h5 { 315 | color: #fff; 316 | color: var(--text-bright); 317 | } 318 | } 319 | 320 | h6 { 321 | color: #000; 322 | color: var(--text-bright); 323 | } 324 | 325 | @media (prefers-color-scheme: dark) { 326 | 327 | h6 { 328 | color: #fff; 329 | color: var(--text-bright); 330 | } 331 | } 332 | 333 | strong { 334 | color: #000; 335 | color: var(--text-bright); 336 | } 337 | 338 | @media (prefers-color-scheme: dark) { 339 | 340 | strong { 341 | color: #fff; 342 | color: var(--text-bright); 343 | } 344 | } 345 | 346 | h1, 347 | h2, 348 | h3, 349 | h4, 350 | h5, 351 | h6, 352 | b, 353 | strong, 354 | th { 355 | font-weight: 600; 356 | } 357 | 358 | q::before { 359 | content: none; 360 | } 361 | 362 | q::after { 363 | content: none; 364 | } 365 | 366 | blockquote { 367 | border-left: 4px solid #0096bfab; 368 | border-left: 4px solid var(--focus); 369 | margin: 1.5em 0; 370 | padding: 0.5em 1em; 371 | font-style: italic; 372 | } 373 | 374 | @media (prefers-color-scheme: dark) { 375 | 376 | blockquote { 377 | border-left: 4px solid #0096bfab; 378 | border-left: 4px solid var(--focus); 379 | } 380 | } 381 | 382 | q { 383 | border-left: 4px solid #0096bfab; 384 | border-left: 4px solid var(--focus); 385 | margin: 1.5em 0; 386 | padding: 0.5em 1em; 387 | font-style: italic; 388 | } 389 | 390 | @media (prefers-color-scheme: dark) { 391 | 392 | q { 393 | border-left: 4px solid #0096bfab; 394 | border-left: 4px solid var(--focus); 395 | } 396 | } 397 | 398 | blockquote > footer { 399 | font-style: normal; 400 | border: 0; 401 | } 402 | 403 | blockquote cite { 404 | font-style: normal; 405 | } 406 | 407 | address { 408 | font-style: normal; 409 | } 410 | 411 | a[href^='mailto\:']::before { 412 | content: '📧 '; 413 | } 414 | 415 | a[href^='tel\:']::before { 416 | content: '📞 '; 417 | } 418 | 419 | a[href^='sms\:']::before { 420 | content: '💬 '; 421 | } 422 | 423 | mark { 424 | background-color: #ff0; 425 | background-color: var(--highlight); 426 | border-radius: 2px; 427 | padding: 0 2px 0 2px; 428 | color: #000; 429 | } 430 | 431 | @media (prefers-color-scheme: dark) { 432 | 433 | mark { 434 | background-color: #efdb43; 435 | background-color: var(--highlight); 436 | } 437 | } 438 | 439 | a > code, 440 | a > strong { 441 | color: inherit; 442 | } 443 | 444 | button, 445 | select, 446 | input[type='submit'], 447 | input[type='reset'], 448 | input[type='button'], 449 | input[type='checkbox'], 450 | input[type='range'], 451 | input[type='radio'] { 452 | cursor: pointer; 453 | } 454 | 455 | input, 456 | select { 457 | display: block; 458 | } 459 | 460 | [type='checkbox'], 461 | [type='radio'] { 462 | display: initial; 463 | } 464 | 465 | input { 466 | color: #1d1d1d; 467 | color: var(--form-text); 468 | background-color: #efefef; 469 | background-color: var(--background); 470 | font-family: inherit; 471 | font-size: inherit; 472 | margin-right: 6px; 473 | margin-bottom: 6px; 474 | padding: 10px; 475 | border: none; 476 | border-radius: 6px; 477 | outline: none; 478 | } 479 | 480 | @media (prefers-color-scheme: dark) { 481 | 482 | input { 483 | background-color: #161f27; 484 | background-color: var(--background); 485 | } 486 | } 487 | 488 | @media (prefers-color-scheme: dark) { 489 | 490 | input { 491 | color: #fff; 492 | color: var(--form-text); 493 | } 494 | } 495 | 496 | button { 497 | color: #1d1d1d; 498 | color: var(--form-text); 499 | background-color: #efefef; 500 | background-color: var(--background); 501 | font-family: inherit; 502 | font-size: inherit; 503 | margin-right: 6px; 504 | margin-bottom: 6px; 505 | padding: 10px; 506 | border: none; 507 | border-radius: 6px; 508 | outline: none; 509 | } 510 | 511 | @media (prefers-color-scheme: dark) { 512 | 513 | button { 514 | background-color: #161f27; 515 | background-color: var(--background); 516 | } 517 | } 518 | 519 | @media (prefers-color-scheme: dark) { 520 | 521 | button { 522 | color: #fff; 523 | color: var(--form-text); 524 | } 525 | } 526 | 527 | textarea { 528 | color: #1d1d1d; 529 | color: var(--form-text); 530 | background-color: #efefef; 531 | background-color: var(--background); 532 | font-family: inherit; 533 | font-size: inherit; 534 | margin-right: 6px; 535 | margin-bottom: 6px; 536 | padding: 10px; 537 | border: none; 538 | border-radius: 6px; 539 | outline: none; 540 | } 541 | 542 | @media (prefers-color-scheme: dark) { 543 | 544 | textarea { 545 | background-color: #161f27; 546 | background-color: var(--background); 547 | } 548 | } 549 | 550 | @media (prefers-color-scheme: dark) { 551 | 552 | textarea { 553 | color: #fff; 554 | color: var(--form-text); 555 | } 556 | } 557 | 558 | select { 559 | color: #1d1d1d; 560 | color: var(--form-text); 561 | background-color: #efefef; 562 | background-color: var(--background); 563 | font-family: inherit; 564 | font-size: inherit; 565 | margin-right: 6px; 566 | margin-bottom: 6px; 567 | padding: 10px; 568 | border: none; 569 | border-radius: 6px; 570 | outline: none; 571 | } 572 | 573 | @media (prefers-color-scheme: dark) { 574 | 575 | select { 576 | background-color: #161f27; 577 | background-color: var(--background); 578 | } 579 | } 580 | 581 | @media (prefers-color-scheme: dark) { 582 | 583 | select { 584 | color: #fff; 585 | color: var(--form-text); 586 | } 587 | } 588 | 589 | button { 590 | background-color: #d0cfcf; 591 | background-color: var(--button-base); 592 | padding-right: 30px; 593 | padding-left: 30px; 594 | } 595 | 596 | @media (prefers-color-scheme: dark) { 597 | 598 | button { 599 | background-color: #0c151c; 600 | background-color: var(--button-base); 601 | } 602 | } 603 | 604 | input[type='submit'] { 605 | background-color: #d0cfcf; 606 | background-color: var(--button-base); 607 | padding-right: 30px; 608 | padding-left: 30px; 609 | } 610 | 611 | @media (prefers-color-scheme: dark) { 612 | 613 | input[type='submit'] { 614 | background-color: #0c151c; 615 | background-color: var(--button-base); 616 | } 617 | } 618 | 619 | input[type='reset'] { 620 | background-color: #d0cfcf; 621 | background-color: var(--button-base); 622 | padding-right: 30px; 623 | padding-left: 30px; 624 | } 625 | 626 | @media (prefers-color-scheme: dark) { 627 | 628 | input[type='reset'] { 629 | background-color: #0c151c; 630 | background-color: var(--button-base); 631 | } 632 | } 633 | 634 | input[type='button'] { 635 | background-color: #d0cfcf; 636 | background-color: var(--button-base); 637 | padding-right: 30px; 638 | padding-left: 30px; 639 | } 640 | 641 | @media (prefers-color-scheme: dark) { 642 | 643 | input[type='button'] { 644 | background-color: #0c151c; 645 | background-color: var(--button-base); 646 | } 647 | } 648 | 649 | button:hover { 650 | background: #9b9b9b; 651 | background: var(--button-hover); 652 | } 653 | 654 | @media (prefers-color-scheme: dark) { 655 | 656 | button:hover { 657 | background: #040a0f; 658 | background: var(--button-hover); 659 | } 660 | } 661 | 662 | input[type='submit']:hover { 663 | background: #9b9b9b; 664 | background: var(--button-hover); 665 | } 666 | 667 | @media (prefers-color-scheme: dark) { 668 | 669 | input[type='submit']:hover { 670 | background: #040a0f; 671 | background: var(--button-hover); 672 | } 673 | } 674 | 675 | input[type='reset']:hover { 676 | background: #9b9b9b; 677 | background: var(--button-hover); 678 | } 679 | 680 | @media (prefers-color-scheme: dark) { 681 | 682 | input[type='reset']:hover { 683 | background: #040a0f; 684 | background: var(--button-hover); 685 | } 686 | } 687 | 688 | input[type='button']:hover { 689 | background: #9b9b9b; 690 | background: var(--button-hover); 691 | } 692 | 693 | @media (prefers-color-scheme: dark) { 694 | 695 | input[type='button']:hover { 696 | background: #040a0f; 697 | background: var(--button-hover); 698 | } 699 | } 700 | 701 | input[type='color'] { 702 | min-height: 2rem; 703 | padding: 8px; 704 | cursor: pointer; 705 | } 706 | 707 | input[type='checkbox'], 708 | input[type='radio'] { 709 | height: 1em; 710 | width: 1em; 711 | } 712 | 713 | input[type='radio'] { 714 | border-radius: 100%; 715 | } 716 | 717 | input { 718 | vertical-align: top; 719 | } 720 | 721 | label { 722 | vertical-align: middle; 723 | margin-bottom: 4px; 724 | display: inline-block; 725 | } 726 | 727 | input:not([type='checkbox']):not([type='radio']), 728 | input[type='range'], 729 | select, 730 | button, 731 | textarea { 732 | -webkit-appearance: none; 733 | } 734 | 735 | textarea { 736 | display: block; 737 | margin-right: 0; 738 | box-sizing: border-box; 739 | resize: vertical; 740 | } 741 | 742 | textarea:not([cols]) { 743 | width: 100%; 744 | } 745 | 746 | textarea:not([rows]) { 747 | min-height: 40px; 748 | height: 140px; 749 | } 750 | 751 | select { 752 | background: #efefef url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 753 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 754 | padding-right: 35px; 755 | } 756 | 757 | @media (prefers-color-scheme: dark) { 758 | 759 | select { 760 | background: #161f27 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 761 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 762 | } 763 | } 764 | 765 | @media (prefers-color-scheme: dark) { 766 | 767 | select { 768 | background: #161f27 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 769 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 770 | } 771 | } 772 | 773 | @media (prefers-color-scheme: dark) { 774 | 775 | select { 776 | background: #161f27 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 777 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 778 | } 779 | } 780 | 781 | @media (prefers-color-scheme: dark) { 782 | 783 | select { 784 | background: #161f27 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; 785 | background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; 786 | } 787 | } 788 | 789 | select::-ms-expand { 790 | display: none; 791 | } 792 | 793 | select[multiple] { 794 | padding-right: 10px; 795 | background-image: none; 796 | overflow-y: auto; 797 | } 798 | 799 | input:focus { 800 | box-shadow: 0 0 0 2px #0096bfab; 801 | box-shadow: 0 0 0 2px var(--focus); 802 | } 803 | 804 | @media (prefers-color-scheme: dark) { 805 | 806 | input:focus { 807 | box-shadow: 0 0 0 2px #0096bfab; 808 | box-shadow: 0 0 0 2px var(--focus); 809 | } 810 | } 811 | 812 | select:focus { 813 | box-shadow: 0 0 0 2px #0096bfab; 814 | box-shadow: 0 0 0 2px var(--focus); 815 | } 816 | 817 | @media (prefers-color-scheme: dark) { 818 | 819 | select:focus { 820 | box-shadow: 0 0 0 2px #0096bfab; 821 | box-shadow: 0 0 0 2px var(--focus); 822 | } 823 | } 824 | 825 | button:focus { 826 | box-shadow: 0 0 0 2px #0096bfab; 827 | box-shadow: 0 0 0 2px var(--focus); 828 | } 829 | 830 | @media (prefers-color-scheme: dark) { 831 | 832 | button:focus { 833 | box-shadow: 0 0 0 2px #0096bfab; 834 | box-shadow: 0 0 0 2px var(--focus); 835 | } 836 | } 837 | 838 | textarea:focus { 839 | box-shadow: 0 0 0 2px #0096bfab; 840 | box-shadow: 0 0 0 2px var(--focus); 841 | } 842 | 843 | @media (prefers-color-scheme: dark) { 844 | 845 | textarea:focus { 846 | box-shadow: 0 0 0 2px #0096bfab; 847 | box-shadow: 0 0 0 2px var(--focus); 848 | } 849 | } 850 | 851 | input[type='checkbox']:active, 852 | input[type='radio']:active, 853 | input[type='submit']:active, 854 | input[type='reset']:active, 855 | input[type='button']:active, 856 | input[type='range']:active, 857 | button:active { 858 | transform: translateY(2px); 859 | } 860 | 861 | input:disabled, 862 | select:disabled, 863 | button:disabled, 864 | textarea:disabled { 865 | cursor: not-allowed; 866 | opacity: 0.5; 867 | } 868 | 869 | ::-moz-placeholder { 870 | color: #949494; 871 | color: var(--form-placeholder); 872 | } 873 | 874 | :-ms-input-placeholder { 875 | color: #949494; 876 | color: var(--form-placeholder); 877 | } 878 | 879 | ::-ms-input-placeholder { 880 | color: #949494; 881 | color: var(--form-placeholder); 882 | } 883 | 884 | ::placeholder { 885 | color: #949494; 886 | color: var(--form-placeholder); 887 | } 888 | 889 | @media (prefers-color-scheme: dark) { 890 | 891 | ::-moz-placeholder { 892 | color: #a9a9a9; 893 | color: var(--form-placeholder); 894 | } 895 | 896 | :-ms-input-placeholder { 897 | color: #a9a9a9; 898 | color: var(--form-placeholder); 899 | } 900 | 901 | ::-ms-input-placeholder { 902 | color: #a9a9a9; 903 | color: var(--form-placeholder); 904 | } 905 | 906 | ::placeholder { 907 | color: #a9a9a9; 908 | color: var(--form-placeholder); 909 | } 910 | } 911 | 912 | fieldset { 913 | border: 1px #0096bfab solid; 914 | border: 1px var(--focus) solid; 915 | border-radius: 6px; 916 | margin: 0; 917 | margin-bottom: 12px; 918 | padding: 10px; 919 | } 920 | 921 | @media (prefers-color-scheme: dark) { 922 | 923 | fieldset { 924 | border: 1px #0096bfab solid; 925 | border: 1px var(--focus) solid; 926 | } 927 | } 928 | 929 | legend { 930 | font-size: 0.9em; 931 | font-weight: 600; 932 | } 933 | 934 | input[type='range'] { 935 | margin: 10px 0; 936 | padding: 10px 0; 937 | background: transparent; 938 | } 939 | 940 | input[type='range']:focus { 941 | outline: none; 942 | } 943 | 944 | input[type='range']::-webkit-slider-runnable-track { 945 | width: 100%; 946 | height: 9.5px; 947 | -webkit-transition: 0.2s; 948 | transition: 0.2s; 949 | background: #efefef; 950 | background: var(--background); 951 | border-radius: 3px; 952 | } 953 | 954 | @media (prefers-color-scheme: dark) { 955 | 956 | input[type='range']::-webkit-slider-runnable-track { 957 | background: #161f27; 958 | background: var(--background); 959 | } 960 | } 961 | 962 | input[type='range']::-webkit-slider-thumb { 963 | box-shadow: 0 1px 1px #000, 0 0 1px #0d0d0d; 964 | height: 20px; 965 | width: 20px; 966 | border-radius: 50%; 967 | background: #dbdbdb; 968 | background: var(--border); 969 | -webkit-appearance: none; 970 | margin-top: -7px; 971 | } 972 | 973 | @media (prefers-color-scheme: dark) { 974 | 975 | input[type='range']::-webkit-slider-thumb { 976 | background: #526980; 977 | background: var(--border); 978 | } 979 | } 980 | 981 | input[type='range']:focus::-webkit-slider-runnable-track { 982 | background: #efefef; 983 | background: var(--background); 984 | } 985 | 986 | @media (prefers-color-scheme: dark) { 987 | 988 | input[type='range']:focus::-webkit-slider-runnable-track { 989 | background: #161f27; 990 | background: var(--background); 991 | } 992 | } 993 | 994 | input[type='range']::-moz-range-track { 995 | width: 100%; 996 | height: 9.5px; 997 | -moz-transition: 0.2s; 998 | transition: 0.2s; 999 | background: #efefef; 1000 | background: var(--background); 1001 | border-radius: 3px; 1002 | } 1003 | 1004 | @media (prefers-color-scheme: dark) { 1005 | 1006 | input[type='range']::-moz-range-track { 1007 | background: #161f27; 1008 | background: var(--background); 1009 | } 1010 | } 1011 | 1012 | input[type='range']::-moz-range-thumb { 1013 | box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; 1014 | height: 20px; 1015 | width: 20px; 1016 | border-radius: 50%; 1017 | background: #dbdbdb; 1018 | background: var(--border); 1019 | } 1020 | 1021 | @media (prefers-color-scheme: dark) { 1022 | 1023 | input[type='range']::-moz-range-thumb { 1024 | background: #526980; 1025 | background: var(--border); 1026 | } 1027 | } 1028 | 1029 | input[type='range']::-ms-track { 1030 | width: 100%; 1031 | height: 9.5px; 1032 | background: transparent; 1033 | border-color: transparent; 1034 | border-width: 16px 0; 1035 | color: transparent; 1036 | } 1037 | 1038 | input[type='range']::-ms-fill-lower { 1039 | background: #efefef; 1040 | background: var(--background); 1041 | border: 0.2px solid #010101; 1042 | border-radius: 3px; 1043 | box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; 1044 | } 1045 | 1046 | @media (prefers-color-scheme: dark) { 1047 | 1048 | input[type='range']::-ms-fill-lower { 1049 | background: #161f27; 1050 | background: var(--background); 1051 | } 1052 | } 1053 | 1054 | input[type='range']::-ms-fill-upper { 1055 | background: #efefef; 1056 | background: var(--background); 1057 | border: 0.2px solid #010101; 1058 | border-radius: 3px; 1059 | box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; 1060 | } 1061 | 1062 | @media (prefers-color-scheme: dark) { 1063 | 1064 | input[type='range']::-ms-fill-upper { 1065 | background: #161f27; 1066 | background: var(--background); 1067 | } 1068 | } 1069 | 1070 | input[type='range']::-ms-thumb { 1071 | box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; 1072 | border: 1px solid #000; 1073 | height: 20px; 1074 | width: 20px; 1075 | border-radius: 50%; 1076 | background: #dbdbdb; 1077 | background: var(--border); 1078 | } 1079 | 1080 | @media (prefers-color-scheme: dark) { 1081 | 1082 | input[type='range']::-ms-thumb { 1083 | background: #526980; 1084 | background: var(--border); 1085 | } 1086 | } 1087 | 1088 | input[type='range']:focus::-ms-fill-lower { 1089 | background: #efefef; 1090 | background: var(--background); 1091 | } 1092 | 1093 | @media (prefers-color-scheme: dark) { 1094 | 1095 | input[type='range']:focus::-ms-fill-lower { 1096 | background: #161f27; 1097 | background: var(--background); 1098 | } 1099 | } 1100 | 1101 | input[type='range']:focus::-ms-fill-upper { 1102 | background: #efefef; 1103 | background: var(--background); 1104 | } 1105 | 1106 | @media (prefers-color-scheme: dark) { 1107 | 1108 | input[type='range']:focus::-ms-fill-upper { 1109 | background: #161f27; 1110 | background: var(--background); 1111 | } 1112 | } 1113 | 1114 | a { 1115 | text-decoration: none; 1116 | color: #0076d1; 1117 | color: var(--links); 1118 | } 1119 | 1120 | @media (prefers-color-scheme: dark) { 1121 | 1122 | a { 1123 | color: #41adff; 1124 | color: var(--links); 1125 | } 1126 | } 1127 | 1128 | a:hover { 1129 | text-decoration: underline; 1130 | } 1131 | 1132 | code { 1133 | background: #efefef; 1134 | background: var(--background); 1135 | color: #000; 1136 | color: var(--code); 1137 | padding: 2.5px 5px; 1138 | border-radius: 6px; 1139 | font-size: 1em; 1140 | } 1141 | 1142 | @media (prefers-color-scheme: dark) { 1143 | 1144 | code { 1145 | color: #ffbe85; 1146 | color: var(--code); 1147 | } 1148 | } 1149 | 1150 | @media (prefers-color-scheme: dark) { 1151 | 1152 | code { 1153 | background: #161f27; 1154 | background: var(--background); 1155 | } 1156 | } 1157 | 1158 | samp { 1159 | background: #efefef; 1160 | background: var(--background); 1161 | color: #000; 1162 | color: var(--code); 1163 | padding: 2.5px 5px; 1164 | border-radius: 6px; 1165 | font-size: 1em; 1166 | } 1167 | 1168 | @media (prefers-color-scheme: dark) { 1169 | 1170 | samp { 1171 | color: #ffbe85; 1172 | color: var(--code); 1173 | } 1174 | } 1175 | 1176 | @media (prefers-color-scheme: dark) { 1177 | 1178 | samp { 1179 | background: #161f27; 1180 | background: var(--background); 1181 | } 1182 | } 1183 | 1184 | time { 1185 | background: #efefef; 1186 | background: var(--background); 1187 | color: #000; 1188 | color: var(--code); 1189 | padding: 2.5px 5px; 1190 | border-radius: 6px; 1191 | font-size: 1em; 1192 | } 1193 | 1194 | @media (prefers-color-scheme: dark) { 1195 | 1196 | time { 1197 | color: #ffbe85; 1198 | color: var(--code); 1199 | } 1200 | } 1201 | 1202 | @media (prefers-color-scheme: dark) { 1203 | 1204 | time { 1205 | background: #161f27; 1206 | background: var(--background); 1207 | } 1208 | } 1209 | 1210 | pre > code { 1211 | padding: 10px; 1212 | display: block; 1213 | overflow-x: auto; 1214 | } 1215 | 1216 | var { 1217 | color: #39a33c; 1218 | color: var(--variable); 1219 | font-style: normal; 1220 | font-family: monospace; 1221 | } 1222 | 1223 | @media (prefers-color-scheme: dark) { 1224 | 1225 | var { 1226 | color: #d941e2; 1227 | color: var(--variable); 1228 | } 1229 | } 1230 | 1231 | kbd { 1232 | background: #efefef; 1233 | background: var(--background); 1234 | border: 1px solid #dbdbdb; 1235 | border: 1px solid var(--border); 1236 | border-radius: 2px; 1237 | color: #363636; 1238 | color: var(--text-main); 1239 | padding: 2px 4px 2px 4px; 1240 | } 1241 | 1242 | @media (prefers-color-scheme: dark) { 1243 | 1244 | kbd { 1245 | color: #dbdbdb; 1246 | color: var(--text-main); 1247 | } 1248 | } 1249 | 1250 | @media (prefers-color-scheme: dark) { 1251 | 1252 | kbd { 1253 | border: 1px solid #526980; 1254 | border: 1px solid var(--border); 1255 | } 1256 | } 1257 | 1258 | @media (prefers-color-scheme: dark) { 1259 | 1260 | kbd { 1261 | background: #161f27; 1262 | background: var(--background); 1263 | } 1264 | } 1265 | 1266 | img, 1267 | video { 1268 | max-width: 100%; 1269 | height: auto; 1270 | } 1271 | 1272 | hr { 1273 | border: none; 1274 | border-top: 1px solid #dbdbdb; 1275 | border-top: 1px solid var(--border); 1276 | } 1277 | 1278 | @media (prefers-color-scheme: dark) { 1279 | 1280 | hr { 1281 | border-top: 1px solid #526980; 1282 | border-top: 1px solid var(--border); 1283 | } 1284 | } 1285 | 1286 | table { 1287 | border-collapse: collapse; 1288 | margin-bottom: 10px; 1289 | width: 100%; 1290 | table-layout: fixed; 1291 | } 1292 | 1293 | table caption { 1294 | text-align: left; 1295 | } 1296 | 1297 | td, 1298 | th { 1299 | padding: 6px; 1300 | text-align: left; 1301 | vertical-align: top; 1302 | word-wrap: break-word; 1303 | } 1304 | 1305 | thead { 1306 | border-bottom: 1px solid #dbdbdb; 1307 | border-bottom: 1px solid var(--border); 1308 | } 1309 | 1310 | @media (prefers-color-scheme: dark) { 1311 | 1312 | thead { 1313 | border-bottom: 1px solid #526980; 1314 | border-bottom: 1px solid var(--border); 1315 | } 1316 | } 1317 | 1318 | tfoot { 1319 | border-top: 1px solid #dbdbdb; 1320 | border-top: 1px solid var(--border); 1321 | } 1322 | 1323 | @media (prefers-color-scheme: dark) { 1324 | 1325 | tfoot { 1326 | border-top: 1px solid #526980; 1327 | border-top: 1px solid var(--border); 1328 | } 1329 | } 1330 | 1331 | tbody tr:nth-child(even) { 1332 | background-color: #efefef; 1333 | background-color: var(--background); 1334 | } 1335 | 1336 | @media (prefers-color-scheme: dark) { 1337 | 1338 | tbody tr:nth-child(even) { 1339 | background-color: #161f27; 1340 | background-color: var(--background); 1341 | } 1342 | } 1343 | 1344 | tbody tr:nth-child(even) button { 1345 | background-color: #f7f7f7; 1346 | background-color: var(--background-alt); 1347 | } 1348 | 1349 | @media (prefers-color-scheme: dark) { 1350 | 1351 | tbody tr:nth-child(even) button { 1352 | background-color: #1a242f; 1353 | background-color: var(--background-alt); 1354 | } 1355 | } 1356 | 1357 | tbody tr:nth-child(even) button:hover { 1358 | background-color: #fff; 1359 | background-color: var(--background-body); 1360 | } 1361 | 1362 | @media (prefers-color-scheme: dark) { 1363 | 1364 | tbody tr:nth-child(even) button:hover { 1365 | background-color: #202b38; 1366 | background-color: var(--background-body); 1367 | } 1368 | } 1369 | 1370 | ::-webkit-scrollbar { 1371 | height: 10px; 1372 | width: 10px; 1373 | } 1374 | 1375 | ::-webkit-scrollbar-track { 1376 | background: #efefef; 1377 | background: var(--background); 1378 | border-radius: 6px; 1379 | } 1380 | 1381 | @media (prefers-color-scheme: dark) { 1382 | 1383 | ::-webkit-scrollbar-track { 1384 | background: #161f27; 1385 | background: var(--background); 1386 | } 1387 | } 1388 | 1389 | ::-webkit-scrollbar-thumb { 1390 | background: rgb(170, 170, 170); 1391 | background: var(--scrollbar-thumb); 1392 | border-radius: 6px; 1393 | } 1394 | 1395 | @media (prefers-color-scheme: dark) { 1396 | 1397 | ::-webkit-scrollbar-thumb { 1398 | background: #040a0f; 1399 | background: var(--scrollbar-thumb); 1400 | } 1401 | } 1402 | 1403 | @media (prefers-color-scheme: dark) { 1404 | 1405 | ::-webkit-scrollbar-thumb { 1406 | background: #040a0f; 1407 | background: var(--scrollbar-thumb); 1408 | } 1409 | } 1410 | 1411 | ::-webkit-scrollbar-thumb:hover { 1412 | background: #9b9b9b; 1413 | background: var(--scrollbar-thumb-hover); 1414 | } 1415 | 1416 | @media (prefers-color-scheme: dark) { 1417 | 1418 | ::-webkit-scrollbar-thumb:hover { 1419 | background: rgb(0, 0, 0); 1420 | background: var(--scrollbar-thumb-hover); 1421 | } 1422 | } 1423 | 1424 | @media (prefers-color-scheme: dark) { 1425 | 1426 | ::-webkit-scrollbar-thumb:hover { 1427 | background: rgb(0, 0, 0); 1428 | background: var(--scrollbar-thumb-hover); 1429 | } 1430 | } 1431 | 1432 | ::-moz-selection { 1433 | background-color: #9e9e9e; 1434 | background-color: var(--selection); 1435 | color: #000; 1436 | color: var(--text-bright); 1437 | } 1438 | 1439 | ::selection { 1440 | background-color: #9e9e9e; 1441 | background-color: var(--selection); 1442 | color: #000; 1443 | color: var(--text-bright); 1444 | } 1445 | 1446 | @media (prefers-color-scheme: dark) { 1447 | 1448 | ::-moz-selection { 1449 | color: #fff; 1450 | color: var(--text-bright); 1451 | } 1452 | 1453 | ::selection { 1454 | color: #fff; 1455 | color: var(--text-bright); 1456 | } 1457 | } 1458 | 1459 | @media (prefers-color-scheme: dark) { 1460 | 1461 | ::-moz-selection { 1462 | background-color: #1c76c5; 1463 | background-color: var(--selection); 1464 | } 1465 | 1466 | ::selection { 1467 | background-color: #1c76c5; 1468 | background-color: var(--selection); 1469 | } 1470 | } 1471 | 1472 | details { 1473 | display: flex; 1474 | flex-direction: column; 1475 | align-items: flex-start; 1476 | background-color: #f7f7f7; 1477 | background-color: var(--background-alt); 1478 | padding: 10px 10px 0; 1479 | margin: 1em 0; 1480 | border-radius: 6px; 1481 | overflow: hidden; 1482 | } 1483 | 1484 | @media (prefers-color-scheme: dark) { 1485 | 1486 | details { 1487 | background-color: #1a242f; 1488 | background-color: var(--background-alt); 1489 | } 1490 | } 1491 | 1492 | details[open] { 1493 | padding: 10px; 1494 | } 1495 | 1496 | details > :last-child { 1497 | margin-bottom: 0; 1498 | } 1499 | 1500 | details[open] summary { 1501 | margin-bottom: 10px; 1502 | } 1503 | 1504 | summary { 1505 | display: list-item; 1506 | background-color: #efefef; 1507 | background-color: var(--background); 1508 | padding: 10px; 1509 | margin: -10px -10px 0; 1510 | cursor: pointer; 1511 | outline: none; 1512 | } 1513 | 1514 | @media (prefers-color-scheme: dark) { 1515 | 1516 | summary { 1517 | background-color: #161f27; 1518 | background-color: var(--background); 1519 | } 1520 | } 1521 | 1522 | summary:hover, 1523 | summary:focus { 1524 | text-decoration: underline; 1525 | } 1526 | 1527 | details > :not(summary) { 1528 | margin-top: 0; 1529 | } 1530 | 1531 | summary::-webkit-details-marker { 1532 | color: #363636; 1533 | color: var(--text-main); 1534 | } 1535 | 1536 | @media (prefers-color-scheme: dark) { 1537 | 1538 | summary::-webkit-details-marker { 1539 | color: #dbdbdb; 1540 | color: var(--text-main); 1541 | } 1542 | } 1543 | 1544 | dialog { 1545 | background-color: #f7f7f7; 1546 | background-color: var(--background-alt); 1547 | color: #363636; 1548 | color: var(--text-main); 1549 | border: none; 1550 | border-radius: 6px; 1551 | border-color: #dbdbdb; 1552 | border-color: var(--border); 1553 | padding: 10px 30px; 1554 | } 1555 | 1556 | @media (prefers-color-scheme: dark) { 1557 | 1558 | dialog { 1559 | border-color: #526980; 1560 | border-color: var(--border); 1561 | } 1562 | } 1563 | 1564 | @media (prefers-color-scheme: dark) { 1565 | 1566 | dialog { 1567 | color: #dbdbdb; 1568 | color: var(--text-main); 1569 | } 1570 | } 1571 | 1572 | @media (prefers-color-scheme: dark) { 1573 | 1574 | dialog { 1575 | background-color: #1a242f; 1576 | background-color: var(--background-alt); 1577 | } 1578 | } 1579 | 1580 | dialog > header:first-child { 1581 | background-color: #efefef; 1582 | background-color: var(--background); 1583 | border-radius: 6px 6px 0 0; 1584 | margin: -10px -30px 10px; 1585 | padding: 10px; 1586 | text-align: center; 1587 | } 1588 | 1589 | @media (prefers-color-scheme: dark) { 1590 | 1591 | dialog > header:first-child { 1592 | background-color: #161f27; 1593 | background-color: var(--background); 1594 | } 1595 | } 1596 | 1597 | dialog::-webkit-backdrop { 1598 | background: #0000009c; 1599 | -webkit-backdrop-filter: blur(4px); 1600 | backdrop-filter: blur(4px); 1601 | } 1602 | 1603 | dialog::backdrop { 1604 | background: #0000009c; 1605 | -webkit-backdrop-filter: blur(4px); 1606 | backdrop-filter: blur(4px); 1607 | } 1608 | 1609 | footer { 1610 | border-top: 1px solid #dbdbdb; 1611 | border-top: 1px solid var(--border); 1612 | padding-top: 10px; 1613 | color: #70777f; 1614 | color: var(--text-muted); 1615 | } 1616 | 1617 | @media (prefers-color-scheme: dark) { 1618 | 1619 | footer { 1620 | color: #a9b1ba; 1621 | color: var(--text-muted); 1622 | } 1623 | } 1624 | 1625 | @media (prefers-color-scheme: dark) { 1626 | 1627 | footer { 1628 | border-top: 1px solid #526980; 1629 | border-top: 1px solid var(--border); 1630 | } 1631 | } 1632 | 1633 | body > footer { 1634 | margin-top: 40px; 1635 | } 1636 | 1637 | @media print { 1638 | body, 1639 | pre, 1640 | code, 1641 | summary, 1642 | details, 1643 | button, 1644 | input, 1645 | textarea { 1646 | background-color: #fff; 1647 | } 1648 | 1649 | button, 1650 | input, 1651 | textarea { 1652 | border: 1px solid #000; 1653 | } 1654 | 1655 | body, 1656 | h1, 1657 | h2, 1658 | h3, 1659 | h4, 1660 | h5, 1661 | h6, 1662 | pre, 1663 | code, 1664 | button, 1665 | input, 1666 | textarea, 1667 | footer, 1668 | summary, 1669 | strong { 1670 | color: #000; 1671 | } 1672 | 1673 | summary::marker { 1674 | color: #000; 1675 | } 1676 | 1677 | summary::-webkit-details-marker { 1678 | color: #000; 1679 | } 1680 | 1681 | tbody tr:nth-child(even) { 1682 | background-color: #f2f2f2; 1683 | } 1684 | 1685 | a { 1686 | color: #00f; 1687 | text-decoration: underline; 1688 | } 1689 | } 1690 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ title }} 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |

{{ title }}

25 |
26 |
27 |
28 | 29 | Create QR-code for this URL. 30 | 31 | 32 | 42 |
43 |
44 | 45 | 53 |
54 |
55 | 56 | 64 |
65 |
66 | 67 |
68 |
69 |
70 | 71 |
72 | 73 | 74 | 75 | 76 |
77 |
78 | 81 | 82 | 83 | --------------------------------------------------------------------------------