diff --git a/server/src/client/assets/css/style.css b/server/src/client/assets/css/style.css index f1c84e8..8a2c82f 100644 --- a/server/src/client/assets/css/style.css +++ b/server/src/client/assets/css/style.css @@ -119,6 +119,28 @@ ul { a { color: unset; text-decoration: unset; + + &:not(:has(time)):is([href^="http"], [href^="mailto"]) { + padding-inline-end: 0.9em; + + &::after { + transition: all 0.3s ease; + position: absolute; + content: ''; + display: inline-block; + width: 1em; + height: 1em; + margin-inline-start: -0.05em; + background-size: 100%; + transform: scale(0.8); + background-image: url("/assets/images/external.svg"); + } + + &:is(:hover, :active, :focus-visible)::after { + filter: invert(100%); + } + + } } form { @@ -135,7 +157,7 @@ input { margin: 0; } -input:is([type="text"], [type="password"], [type="search"]) { +input:is([type="text"], [type="password"], [type="search"], [type="email"], ) { padding: 0.5ch 1ch; &:focus, @@ -347,7 +369,60 @@ header { } +footer { + border-block-start: var(--border); + display: flex; + min-block-size: calc(var(--header-size) * 2.5); + padding: 1rem; + gap: 2rem; + flex-direction: column; + align-items: start; + justify-content: start; + + &>* { + max-inline-size: 100%; + display: flex; + flex-direction: column; + justify-content: start; + } + + line-height: 1.6em; + + form { + display: flex; + flex-direction: column; + + & div { + display: flex; + max-inline-size: 100%; + + &>* { + min-inline-size: 0; + } + + input[type="submit"] { + min-inline-size: min-content; + } + } + + } + + h1, + ul { + margin-block: 0 1rem; + } + + p, + li, + input { + margin-block: 0.3rem; + } + + +} + main { + min-block-size: 100svb; margin-block: var(--default-padding) 40svb; margin-inline: auto; max-inline-size: min(60ch, 80%); @@ -422,6 +497,15 @@ main { } } + footer { + flex-direction: row; + + &>* { + max-inline-size: 40ch; + min-inline-size: 30ch; + } + } + main { margin-block: var(--default-padding); } diff --git a/server/src/client/assets/images/external.svg b/server/src/client/assets/images/external.svg new file mode 100644 index 0000000..1e7e0f3 --- /dev/null +++ b/server/src/client/assets/images/external.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/server/src/html.rs b/server/src/html.rs index 4c99f9e..77a4b77 100644 --- a/server/src/html.rs +++ b/server/src/html.rs @@ -72,6 +72,12 @@ where } Ok(()) }), + element!("site-footer", |site_footer| { + if settings.site_footer { + site_footer.replace(template!("site-footer"), ContentType::Html); + } + Ok(()) + }), element!("br", |br| { br.remove(); Ok(()) @@ -187,7 +193,7 @@ where .unwrap_or_default(); make_page( html, - PageSettings::new("Editor", Some(vec!["/assets/css/editor.css"]), true), + PageSettings::new("Editor", Some(vec!["/assets/css/editor.css"]), true, false), ) } @@ -229,7 +235,7 @@ pub(crate) fn login_status(next: &str, success: bool) -> String { .unwrap_or_default(); make_page( html, - PageSettings::new("Login", Some(vec!["/assets/css/login.css"]), false), + PageSettings::new("Login", Some(vec!["/assets/css/login.css"]), false, false), ) } @@ -237,7 +243,7 @@ pub(crate) async fn admin_page(session_id: i64, db: &BlogDb) -> String { let content = admin_widgets(template!("admin"), session_id, db).await; make_page( &content, - PageSettings::new("Admin", Some(vec!["/assets/css/admin.css"]), true), + PageSettings::new("Admin", Some(vec!["/assets/css/admin.css"]), true, false), ) } @@ -558,7 +564,7 @@ pub(crate) async fn blog_roll(db: &BlogDb) -> String { make_page( blog_roll_html, - PageSettings::new("Blog", Some(vec!["/assets/css/blog.css"]), true), + PageSettings::new("Blog", Some(vec!["/assets/css/blog.css"]), true, true), ) } @@ -653,6 +659,7 @@ pub(crate) async fn search_page( ["Results for “", &query, "”"].concat(), Some(vec!["/assets/css/blog.css"]), true, + true, ), ) } @@ -694,6 +701,7 @@ pub(crate) struct PageSettings { title: String, stylesheets: Option>, site_header: bool, + site_footer: bool, } impl PageSettings { @@ -705,9 +713,15 @@ impl PageSettings { title: title.to_string(), stylesheets: None, site_header: true, + site_footer: true, } } - pub(crate) fn new(title: S, stylesheets: Option>, site_header: bool) -> Self + pub(crate) fn new( + title: S, + stylesheets: Option>, + site_header: bool, + site_footer: bool, + ) -> Self where S: ToString, T: ToString, @@ -718,6 +732,7 @@ impl PageSettings { title: title.to_string(), stylesheets, site_header, + site_footer, } } } @@ -754,6 +769,12 @@ where .unwrap_or_default() } +// fn footer(input: S) -> String +// where +// S: AsRef, +// { +// } + pub(crate) fn sanitize>(input: S) -> String { rewrite_str( input.as_ref(), diff --git a/server/src/templates/default.html b/server/src/templates/default.html index 535d7ae..4d20c4a 100644 --- a/server/src/templates/default.html +++ b/server/src/templates/default.html @@ -11,6 +11,7 @@
+ diff --git a/server/src/templates/site-footer.html b/server/src/templates/site-footer.html new file mode 100644 index 0000000..8bc559b --- /dev/null +++ b/server/src/templates/site-footer.html @@ -0,0 +1,53 @@ +