From 350829c3c8e07862765011cacd4a20dac6f5d8f3 Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sun, 22 Oct 2023 19:20:34 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BF=EF=B8=8F=20Improve=20keyboard=20acces?= =?UTF-8?q?sibility:=20content=20skip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pagetop/src/core/theme/definition.rs | 21 +++++++++---- pagetop/src/locale/en-US/theme.ftl | 2 ++ pagetop/src/locale/es-ES/theme.ftl | 2 ++ pagetop/src/response/page.rs | 14 ++++++++- pagetop/static/base/css/root.css | 44 ++++++++++++++++------------ pagetop/static/base/css/styles.css | 30 ++++++++++++++++++- 6 files changed, 86 insertions(+), 27 deletions(-) diff --git a/pagetop/src/core/theme/definition.rs b/pagetop/src/core/theme/definition.rs index 9cba6852..c5b1be5a 100644 --- a/pagetop/src/core/theme/definition.rs +++ b/pagetop/src/core/theme/definition.rs @@ -1,8 +1,9 @@ use crate::core::component::{ComponentTrait, Context}; use crate::core::module::ModuleTrait; use crate::html::{html, Favicon, Markup}; +use crate::locale::L10n; use crate::response::page::Page; -use crate::{config, LOCALES_PAGETOP}; +use crate::{concat_string, config}; pub type ThemeRef = &'static dyn ThemeTrait; @@ -11,11 +12,11 @@ pub trait ThemeTrait: ModuleTrait + Send + Sync { #[rustfmt::skip] fn regions(&self) -> Vec<(&'static str, L10n)> { vec![ - ("header", L10n::t("header", &LOCALES_PAGETOP)), - ("pagetop", L10n::t("pagetop", &LOCALES_PAGETOP)), - ("content", L10n::t("content", &LOCALES_PAGETOP)), - ("sidebar", L10n::t("sidebar", &LOCALES_PAGETOP)), - ("footer", L10n::t("footer", &LOCALES_PAGETOP)), + ("header", L10n::l("header")), + ("pagetop", L10n::l("pagetop")), + ("content", L10n::l("content")), + ("sidebar", L10n::l("sidebar")), + ("footer", L10n::l("footer")), ] } @@ -28,8 +29,16 @@ pub trait ThemeTrait: ModuleTrait + Send + Sync { let content = page.prepare_region("content"); let sidebar = page.prepare_region("sidebar"); let footer = page.prepare_region("footer"); + + let skip_to = concat_string!("#", page.skip_to().get().unwrap_or("content".to_owned())); + html! { body class=[page.body_classes().get()] { + @if let Some(skip) = L10n::l("skip_to_content").using(page.context().langid()) { + div class="pt-body__skip" { + a href=(skip_to) { (skip) } + } + } div class="pt-body__wrapper" { div class="pt-body__regions" { (header.unwrap_or_default()) diff --git a/pagetop/src/locale/en-US/theme.ftl b/pagetop/src/locale/en-US/theme.ftl index a43e60ac..97076794 100644 --- a/pagetop/src/locale/en-US/theme.ftl +++ b/pagetop/src/locale/en-US/theme.ftl @@ -3,3 +3,5 @@ pagetop = Page Top content = Content sidebar = Sidebar footer = Footer + +skip_to_content = Skip to main content (Press Enter) diff --git a/pagetop/src/locale/es-ES/theme.ftl b/pagetop/src/locale/es-ES/theme.ftl index 75fe1bb3..202bd402 100644 --- a/pagetop/src/locale/es-ES/theme.ftl +++ b/pagetop/src/locale/es-ES/theme.ftl @@ -3,3 +3,5 @@ pagetop = Superior content = Contenido sidebar = Barra lateral footer = Pie + +skip_to_content = Ir al contenido principal (Pulsar Intro) diff --git a/pagetop/src/response/page.rs b/pagetop/src/response/page.rs index 47b674ca..1ce43fdb 100644 --- a/pagetop/src/response/page.rs +++ b/pagetop/src/response/page.rs @@ -22,6 +22,7 @@ pub struct Page { context : Context, body_classes: OptionClasses, regions : ComponentsRegions, + skip_to : OptionId, template : String, } @@ -35,8 +36,9 @@ impl Page { properties : Vec::new(), favicon : None, context : Context::new(request), - body_classes: OptionClasses::new().with_value(ClassesOp::SetDefault, "body"), + body_classes: OptionClasses::new(), regions : ComponentsRegions::new(), + skip_to : OptionId::new(), template : "default".to_owned(), } } @@ -91,6 +93,12 @@ impl Page { self } + #[fn_builder] + pub fn alter_skip_to(&mut self, id: impl Into) -> &mut Self { + self.skip_to.alter_value(id); + self + } + #[fn_builder] pub fn alter_template(&mut self, template: &str) -> &mut Self { self.template = template.to_owned(); @@ -127,6 +135,10 @@ impl Page { &self.body_classes } + pub fn skip_to(&self) -> &OptionId { + &self.skip_to + } + pub fn template(&self) -> &str { self.template.as_str() } diff --git a/pagetop/static/base/css/root.css b/pagetop/static/base/css/root.css index 6375ccf8..2a5060bb 100644 --- a/pagetop/static/base/css/root.css +++ b/pagetop/static/base/css/root.css @@ -12,9 +12,7 @@ --pt-font-size-xxs: 0.75rem; --pt-font-weight: 400; --pt-line-height-base: 1.5; - --pt-color-bg: #fff; - --pt-color: #212529; - --pt-max-width: 80rem; + --pt-max-width: 90rem; /* --pt-color-rgb: 33,37,41; --pt-main--bg-rgb: 255,255,255; @@ -75,11 +73,11 @@ :root { /* --pt-gap-0-25: calc(0.25 * var(--pt-gap)); - --pt-gap-0-5: calc(0.5 * var(--pt-gap)); */ + --pt-gap-0-5: calc(0.5 * var(--pt-gap)); --pt-gap-0-75: calc(0.75 * var(--pt-gap)); -/* --pt-gap-1-5: calc(1.5 * var(--pt-gap)); +/* --pt-gap-2: calc(2 * var(--pt-gap)); --pt-gap-2-5: calc(2.5 * var(--pt-gap)); --pt-gap-3: calc(3 * var(--pt-gap)); @@ -92,19 +90,26 @@ --pt-gap-10: calc(10 * var(--pt-gap)); --pt-gap-11: calc(11 * var(--pt-gap)); --pt-gap-12: calc(12 * var(--pt-gap)); +*/ + + --pt-color--gray-hue: 201; + --pt-color--gray-saturation: 15%; + --pt-color--gray-5: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),5%); + --pt-color--gray-10: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),11%); + --pt-color--gray-20: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),20%); + --pt-color--gray-45: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),44%); + --pt-color--gray-60: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),57%); + --pt-color--gray-65: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),63%); + --pt-color--gray-70: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),72%); + --pt-color--gray-90: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),88%); + --pt-color--gray-95: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),93%); + --pt-color--gray-100: hsl(var(--pt-color--gray-hue),var(--pt-color--gray-saturation),97%); + + --pt-color--white: #fff; + --pt-color--bg: var(--pt-color--white); + --pt-color: #212529; + /* - --color--gray-hue: 201; - --color--gray-saturation: 15%; - --color--gray-5: hsl(var(--color--gray-hue),var(--color--gray-saturation),5%); - --color--gray-10: hsl(var(--color--gray-hue),var(--color--gray-saturation),11%); - --color--gray-20: hsl(var(--color--gray-hue),var(--color--gray-saturation),20%); - --color--gray-45: hsl(var(--color--gray-hue),var(--color--gray-saturation),44%); - --color--gray-60: hsl(var(--color--gray-hue),var(--color--gray-saturation),57%); - --color--gray-65: hsl(var(--color--gray-hue),var(--color--gray-saturation),63%); - --color--gray-70: hsl(var(--color--gray-hue),var(--color--gray-saturation),72%); - --color--gray-90: hsl(var(--color--gray-hue),var(--color--gray-saturation),88%); - --color--gray-95: hsl(var(--color--gray-hue),var(--color--gray-saturation),93%); - --color--gray-100: hsl(var(--color--gray-hue),var(--color--gray-saturation),97%); --color--primary-hue: 202; --color--primary-saturation: 79%; --color--primary-lightness: 50; @@ -119,7 +124,8 @@ --color-text-primary-medium: var(--color--primary-40); --color-text-primary-loud: var(--color--primary-30); --color--black: #000; - --color--white: #fff; +*/ +/* --color--red: #e33f1e; --color--gold: #fdca40; --color--green: #3fa21c; @@ -129,7 +135,7 @@ --pt-border-radius: 0.1875rem; - --pt-menu--color-bg: #fafafa; + --pt-menu--color-bg: var(--pt-color--gray-100); --pt-menu--color-highlight: #e91e63; --pt-menu--color-border: rgba(0, 0, 0, 0.1); --pt-menu--color-shadow: rgba(0, 0, 0, 0.06); diff --git a/pagetop/static/base/css/styles.css b/pagetop/static/base/css/styles.css index 234b8b3c..165a9536 100644 --- a/pagetop/static/base/css/styles.css +++ b/pagetop/static/base/css/styles.css @@ -1,3 +1,7 @@ +html { + scroll-behavior: smooth; +} + body { margin: 0; font-family: var(--pt-font-family); @@ -5,12 +9,36 @@ body { font-weight: var(--pt-font-weight); line-height: var(--pt-line-height-base); color: var(--pt-color); - background-color: var(--pt-color-bg); + background-color: var(--pt-color--bg); -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; } +.pt-body__skip { + display: flex; + justify-content: center; +} +.pt-body__skip a { + display: block; + padding: var(--pt-gap-0-5) var(--pt-gap-1-5); + color: var(--pt-color--white); + background-color: var(--pt-color--gray-5); + text-decoration: none; + outline: 0; + position: absolute; + transform: translateY(-100%); + transition: all 0.3s ease-in-out; + z-index: 9999; +} +.pt-body__skip a:after { + content: "\0020 ➔"; +} +.pt-body__skip a:focus { + transform: translateY(0%); +} + .pt-body__wrapper { + width: 100%; max-width: var(--pt-max-width); margin: 0 auto; }