Make the login page much prettier

This commit is contained in:
Olivier 'reivilibre 2025-06-05 22:05:51 +01:00
parent d7baf055ac
commit 8c565f1d2d
15 changed files with 224 additions and 16 deletions

4
.gitignore vendored
View File

@ -5,8 +5,8 @@
/book
/static/light.css
/static/dark.css
/static/main.css
/static/main.css.map
# Devenv
.devenv*

View File

@ -25,7 +25,9 @@
# Snapshot testing
pkgs.cargo-insta
pkgs.grass-sass
# pkgs.grass-sass not compatible, c.f. https://github.com/connorskees/grass/issues/105
pkgs.dart-sass
pkgs.entr
pkgs.mdbook

5
sample/generate_keypair.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
cd "$(dirname "$0")"
openssl genrsa -out keypair.pem 4096

52
sample/keypair.pem Normal file
View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
ygZRXTLCUEsDBG0wZRObqwDHneHGTQ==
-----END PRIVATE KEY-----

5
sample/run.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
cd "$(dirname "$0")"
HORNBEAM_BASE=.. exec cargo run -- --config "config.toml" --secrets "secrets.toml" serve

View File

@ -3,6 +3,13 @@ set -eu
cd "$(dirname "$0")/.."
# what sass CLI to use
# grass is currently incompatible: https://github.com/connorskees/grass/issues/105
# so we use dart-sass
SASS_IMPL=${SASS_IMPL:-dart-sass}
while true; do
find ./src_scss | entr -d bash -c 'for theme in "dark" "light"; do grass --style compressed src_scss/$theme.scss static/$theme.css; done'
# find ./src_scss | entr -d bash -c 'for theme in "dark" "light"; do grass --style compressed src_scss/$theme.scss static/$theme.css; done'
find ./src_scss | entr -d $SASS_IMPL -I deps/picocss/scss --style compressed src_scss/main.scss static/main.css; done
done

15
src_scss/_centred.scss Normal file
View File

@ -0,0 +1,15 @@
body.centred {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
body.centred * {
flex: 1;
max-width: 95vw;
@media (min-width: 768px) {
max-width: min(80vw, 1280px);
}
}

56
src_scss/main.scss Normal file
View File

@ -0,0 +1,56 @@
@use "pico" with (
$theme-color: "pumpkin",
// Consider which modules and things we actually want enabled.
$enable-semantic-container: true,
$enable-classes: false,
$modules: (
// Theme
"themes/default": true,
// Layout
"layout/document": true,
"layout/landmarks": true,
"layout/container": true,
"layout/section": true,
"layout/grid": true,
"layout/overflow-auto": true,
// Content
"content/link": true,
"content/typography": true,
"content/embedded": false,
"content/button": true,
"content/table": true,
"content/code": false,
"content/figure": false,
"content/misc": true,
// Forms
"forms/basics": true,
"forms/checkbox-radio-switch": false,
"forms/input-color": false,
"forms/input-date": false,
"forms/input-file": false,
"forms/input-range": false,
"forms/input-search": false,
// Components
"components/accordion": false,
"components/card": false,
"components/dropdown": false,
"components/group": false,
"components/loading": false,
"components/modal": false,
"components/nav": false,
"components/progress": false,
"components/tooltip": false,
// Utilities
"utilities/accessibility": true,
"utilities/reduce-motion": true
)
);
@use "_centred.scss";

View File

@ -1,11 +1,23 @@
html
declare
// = false
param $centred = 0 > 1
html {lang="en"}
head
meta {charset="UTF-8"}
meta {name="viewport", content="width=device-width, initial-scale=1"}
meta {name="color-scheme", content="light dark"}
title
optional slot :title
link {rel="stylesheet", href="/statics/light.css"} // TODO support theming
link {rel="stylesheet", href="/static/main.css"}
body
slot :main
if $centred
body.centred
slot :main
else
body
slot :main

View File

@ -35,3 +35,12 @@ for $validator in $form.info.field_validators($name)
input {$type, $name, $minlength?, $maxlength?, $required?, $pattern?}
set $errs = $form.errors.__get($name)
if $errs.len() != 0
@form_errors{count = $errs.len()}
ul
for $err in $errs
li
"${$err.error_code()}"

View File

@ -1,11 +1,22 @@
BarePage
BarePage {centred = 1 > 0}
form {method = "POST"}
"UN"
Text {$form, name = "username"}
" PW"
Text {$form, name = "password", type = "password"}
" "
set $errs = $form.errors.form_wide
if $errs.len() != 0
@form_errors{count = $errs.len()}
ul
for $err in $errs
li
@login_error{errcode = $err.error_code()}
fieldset
label
@form_username
Text {$form, name = "username"}
label
@form_password
Text {$form, name = "password", type = "password"}
input {type = "hidden", name = "xsrf", value = $xsrf_token}
button {type = "submit"}
"click here to login"
" (temporary form)"
@form_login

17
translations/en/login.ftl Normal file
View File

@ -0,0 +1,17 @@
form_errors = There {$count ->
[one] was an error
*[other] were {$count} errors
}:
form_login = Log in
form_username = Username
form_password = Password
login_error = {$errcode ->
[invalid_credentials] The wrong username and/or password were provided.
*[other] $errcode
}

17
translations/fr/login.ftl Normal file
View File

@ -0,0 +1,17 @@
form_errors = Il y a eu {$count ->
[one] une erreur
*[other] {$count} erreurs
}:
form_login = Se connecter
form_username = Nom d'utilisateur
form_password = Mot de passe
login_error = {$errcode ->
[invalid_credentials] Les credentiales sont mauvaises. (?TODO)
*[other] {$errcode}
}