Compare commits

..

2 commits

5 changed files with 99 additions and 82 deletions

View file

@ -1,7 +1,7 @@
# 🔃 Dependencias # 🔃 Dependencias
PageTop está basado en [Rust](https://www.rust-lang.org/) y crece a hombros de gigantes aprovechando PageTop está basado en [Rust](https://www.rust-lang.org/) y crece a hombros de gigantes aprovechando
algunas de las librerías más robustas y populares del [ecosistema Rust](https://lib.rs) como son: algunas de las librerías más robustas y populares del [ecosistema Rust](https://lib.rs/) como son:
* [Actix Web](https://actix.rs/) para los servicios web. * [Actix Web](https://actix.rs/) para los servicios web.
* [Config](https://docs.rs/config) para cargar y procesar las opciones de configuración. * [Config](https://docs.rs/config) para cargar y procesar las opciones de configuración.
@ -26,8 +26,15 @@ para mostrar un banner de presentación en el terminal con el nombre de la aplic
* [starwars.flf](http://www.figlet.org/fontdb_example.cgi?font=starwars.flf) de *Ryan Youck* * [starwars.flf](http://www.figlet.org/fontdb_example.cgi?font=starwars.flf) de *Ryan Youck*
# 🎨 Icono # 🎨 CSS
"La Criatura" sonriente es una simpática creación de [Webalys](https://www.iconfinder.com/webalys). La extensión `pagetop-bootsier` es un tema que integra [Bootstrap 5.3.8](https://getbootstrap.com/)
para los estilos y componentes de la interfaz. Bootstrap está distribuido bajo licencia
[MIT](https://github.com/twbs/bootstrap/blob/main/LICENSE).
# 👾 Icono
"La Mascota" sonriente es una simpática creación de [Webalys](https://www.iconfinder.com/webalys).
Forma parte de su colección [Nasty Icons](https://www.iconfinder.com/iconsets/nasty), disponible en Forma parte de su colección [Nasty Icons](https://www.iconfinder.com/iconsets/nasty), disponible en
[ICONFINDER](https://www.iconfinder.com). [ICONFINDER](https://www.iconfinder.com).

View file

@ -22,12 +22,13 @@ pub enum IntroOpening {
/// ///
/// Usa la imagen de PageTop para mostrar: /// Usa la imagen de PageTop para mostrar:
/// ///
/// - Una **figura decorativa** (que incluye el *monster* de PageTop) antecediendo al contenido. /// - Una **figura decorativa** (que incluye la *mascota* de PageTop) antecediendo al contenido.
/// - Una vista destacada del **título** de la página con un **eslogan** de presentación. /// - Una vista destacada del **título** de la página con un **eslogan** de presentación.
/// - Un **botón opcional** de llamada a la acción con texto y enlace configurables. /// - Un **botón opcional** de llamada a la acción con texto y enlace configurables.
/// - Un **área para la presentación de contenidos**, con *badges* informativos de PageTop (si se /// - Un **área para la presentación de contenidos**, con *badges* informativos de PageTop (si se
/// opta por [`IntroOpening::PageTop`]) y bloques ([`Block`](crate::base::component::Block)) de /// opta por [`IntroOpening::PageTop`]) y bloques ([`Block`](crate::base::component::Block)) de
/// contenido libre para crear párrafos vistosos de texto. Aunque admite todo tipo de componentes. /// contenido libre. Los párrafos que tengan la clase `.intro-text-lead` se mostrarán con una
/// tipografía ampliada, ideal para presentaciones breves e impactantes.
/// ///
/// # Ejemplos /// # Ejemplos
/// ///
@ -70,7 +71,9 @@ pub enum IntroOpening {
/// .with_title(L10n::l("intro_custom_block_title")) /// .with_title(L10n::l("intro_custom_block_title"))
/// .with_child(Html::with(move |cx| { /// .with_child(Html::with(move |cx| {
/// html! { /// html! {
/// p { (L10n::l("intro_custom_paragraph_1").using(cx)) } /// p class="intro-text-lead" {
/// (L10n::l("intro_custom_paragraph_1").using(cx))
/// }
/// p { (L10n::l("intro_custom_paragraph_2").using(cx)) } /// p { (L10n::l("intro_custom_paragraph_2").using(cx)) }
/// } /// }
/// })), /// })),
@ -135,39 +138,41 @@ impl Component for Intro {
Ok(html! { Ok(html! {
div class="intro" { div class="intro" {
div class="intro-header" { div class="intro-header" {
section class="intro-header__body" { section class="intro-header-body" {
h1 class="intro-header__title" { h1 class="intro-header-title" {
span { (self.title().using(cx)) } span { (self.title().using(cx)) }
(self.slogan().using(cx)) (self.slogan().using(cx))
} }
} }
aside class="intro-header__image" aria-hidden="true" { aside class="intro-header-img" aria-hidden="true" {
div class="intro-header__monster" { div class="intro-header-mascot" {
(PageTopSvg::Color.render(cx)) (PageTopSvg::Color.render(cx))
} }
} }
} }
div class="intro-content" { div class="intro-content" {
section class="intro-content__body" { section class="intro-content-body" {
div class="intro-text" { div class="intro-text" {
@if let Some((txt, lnk)) = self.button() { @if let Some((txt, lnk)) = self.button() {
div class="intro-button" { div class="intro-button" {
a a
class="intro-button__link" class="intro-button-link"
href=((lnk)(cx)) href=((lnk)(cx))
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
{ {
span {} span {} span {} span {} span {} span {}
div class="intro-button__text" { div class="intro-button-text" {
(txt.using(cx)) (txt.using(cx))
} }
} }
} }
} }
div class="intro-text__children" { div class="intro-text-body" {
@if *self.opening() == IntroOpening::PageTop { @if *self.opening() == IntroOpening::PageTop {
p { (L10n::l("intro_text1").using(cx)) } p class="intro-text-lead" {
(L10n::l("intro_text1").using(cx))
}
div id="intro-badges" { div id="intro-badges" {
img img
src="https://img.shields.io/crates/v/pagetop.svg?label=PageTop&style=for-the-badge" src="https://img.shields.io/crates/v/pagetop.svg?label=PageTop&style=for-the-badge"
@ -182,7 +187,9 @@ impl Component for Intro {
)) ))
alt=[L10n::l("intro_license_label").lookup(cx)] {} alt=[L10n::l("intro_license_label").lookup(cx)] {}
} }
p { (L10n::l("intro_text2").using(cx)) } p class="intro-text-lead" {
(L10n::l("intro_text2").using(cx))
}
} }
(self.children().render(cx)) (self.children().render(cx))
} }
@ -190,11 +197,11 @@ impl Component for Intro {
} }
} }
div class="intro-footer" { div class="intro-footer" {
section class="intro-footer__body" { section class="intro-footer-body" {
div class="intro-footer__logo" { div class="intro-footer-logo" {
(PageTopSvg::LineLight.render(cx)) (PageTopSvg::LineLight.render(cx))
} }
div class="intro-footer__links" { div class="intro-footer-links" {
a href="https://crates.io/crates/pagetop" target="_blank" rel="noopener noreferrer" { ("Crates.io") } a href="https://crates.io/crates/pagetop" target="_blank" rel="noopener noreferrer" { ("Crates.io") }
a href="https://docs.rs/pagetop" target="_blank" rel="noopener noreferrer" { ("Docs.rs") } a href="https://docs.rs/pagetop" target="_blank" rel="noopener noreferrer" { ("Docs.rs") }
a href="https://git.cillero.es/manuelcillero/pagetop" target="_blank" rel="noopener noreferrer" { (L10n::l("intro_code").using(cx)) } a href="https://git.cillero.es/manuelcillero/pagetop" target="_blank" rel="noopener noreferrer" { (L10n::l("intro_code").using(cx)) }

View file

@ -37,8 +37,12 @@ async fn home(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
.with_title(L10n::l("welcome_status_title")) .with_title(L10n::l("welcome_status_title"))
.with_child(Html::with(move |cx| { .with_child(Html::with(move |cx| {
html! { html! {
p { (L10n::l("welcome_status_1").using(cx)) } p class="intro-text-lead" {
p { (L10n::l("welcome_status_2").using(cx)) } (L10n::l("welcome_status_1").using(cx))
}
p class="intro-text-lead" {
(L10n::l("welcome_status_2").using(cx))
}
} }
})), })),
) )
@ -47,8 +51,12 @@ async fn home(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
.with_title(L10n::l("welcome_support_title")) .with_title(L10n::l("welcome_support_title"))
.with_child(Html::with(move |cx| { .with_child(Html::with(move |cx| {
html! { html! {
p { (L10n::l("welcome_support_1").using(cx)) } p class="intro-text-lead" {
p { (L10n::l("welcome_support_2").with_arg("app", app).using(cx)) } (L10n::l("welcome_support_1").using(cx))
}
p class="intro-text-lead" {
(L10n::l("welcome_support_2").with_arg("app", app).using(cx))
}
} }
})), })),
), ),

View file

@ -199,11 +199,6 @@ pub trait Theme: Extension + Send + Sync {
/// El tema puede mutar el componente antes de devolver `None`, dejando que otro nivel de la /// El tema puede mutar el componente antes de devolver `None`, dejando que otro nivel de la
/// cadena se encargue del renderizado. /// cadena se encargue del renderizado.
/// - `Some(Ok(markup))` con el HTML generado por el tema para el componente. /// - `Some(Ok(markup))` con el HTML generado por el tema para el componente.
///
/// > **Nota para componentes en región:** los componentes registrados con `InRegion` son
/// > instancias únicas compartidas entre peticiones. Cualquier mutación realizada aquí debe
/// > ser idempotente — sobrescribir valores, nunca acumular — o el estado se corromperá a
/// > partir de la segunda petición.
/// - `Some(Err(e))` si el tema intentó renderizarlo pero falló. /// - `Some(Err(e))` si el tema intentó renderizarlo pero falló.
/// ///
/// Para renderizar usa [`render_component!`], que devuelve `None` si ningún tipo coincide. Para /// Para renderizar usa [`render_component!`], que devuelve `None` si ningún tipo coincide. Para

View file

@ -77,11 +77,11 @@ body {
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
.intro-header__body { .intro-header-body {
padding: 0; padding: 0;
background: none; background: none;
} }
.intro-header__title { .intro-header-title {
margin: 0 0 0 1.5rem; margin: 0 0 0 1.5rem;
text-align: left; text-align: left;
display: flex; display: flex;
@ -95,7 +95,7 @@ body {
line-height: 110%; line-height: 110%;
text-shadow: 0 0.125rem 0.1875rem rgba(0, 0, 0, 0.3); text-shadow: 0 0.125rem 0.1875rem rgba(0, 0, 0, 0.3);
} }
.intro-header__title > span { .intro-header-title > span {
background: linear-gradient(180deg, #ddff95 30%, #ffb84b 100%); background: linear-gradient(180deg, #ddff95 30%, #ffb84b 100%);
background-clip: text; background-clip: text;
-webkit-background-clip: text; -webkit-background-clip: text;
@ -106,13 +106,13 @@ body {
line-height: 135%; line-height: 135%;
text-shadow: none; text-shadow: none;
} }
.intro-header__image { .intro-header-img {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
text-align: right; text-align: right;
width: 100%; width: 100%;
} }
.intro-header__monster { .intro-header-mascot {
margin: 2rem; margin: 2rem;
flex-shrink: 1; flex-shrink: 1;
width: 280px; width: 280px;
@ -123,13 +123,13 @@ body {
background-image: var(--intro-bg-img); background-image: var(--intro-bg-img);
background-image: var(--intro-bg-img-set); background-image: var(--intro-bg-img-set);
} }
.intro-header__title { .intro-header-title {
padding: 0 2rem 2.6rem 2rem; padding: 0 2rem 2.6rem 2rem;
} }
.intro-header__image { .intro-header-img {
justify-content: flex-end; justify-content: flex-end;
} }
.intro-header__monster { .intro-header-mascot {
margin-right: 12rem; margin-right: 12rem;
} }
} }
@ -143,14 +143,14 @@ body {
margin-top: 1.6rem; margin-top: 1.6rem;
width: 100%; width: 100%;
} }
.intro-content__body { .intro-content-body {
box-sizing: border-box; box-sizing: border-box;
max-width: 80rem; max-width: 80rem;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.intro-content__body:before, .intro-content-body:before,
.intro-content__body:after { .intro-content-body:after {
content: ''; content: '';
position: absolute; position: absolute;
left: 0; left: 0;
@ -161,14 +161,14 @@ body {
opacity: 0.8; opacity: 0.8;
inset: unset; inset: unset;
} }
.intro-content__body:before { .intro-content-body:before {
top: -1rem; top: -1rem;
} }
.intro-content__body:after { .intro-content-body:after {
bottom: -1rem; bottom: -1rem;
} }
@media (width <= 48rem) { @media (width <= 48rem) {
.intro-content__body { .intro-content-body {
margin-top: -9.8rem; margin-top: -9.8rem;
} }
} }
@ -176,11 +176,11 @@ body {
.intro-content { .intro-content {
margin-top: 0; margin-top: 0;
} }
.intro-content__body { .intro-content-body {
margin-top: -5.7rem; margin-top: -5.7rem;
} }
.intro-content__body:before, .intro-content-body:before,
.intro-content__body:after { .intro-content-body:after {
inset: 11.75rem; inset: 11.75rem;
} }
} }
@ -189,7 +189,7 @@ body {
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
} }
.intro-button__link { .intro-button-link {
background: #7f1d1d; background: #7f1d1d;
background-image: linear-gradient(to bottom, rgba(255,0,0,0.8), rgba(255,255,255,0)); background-image: linear-gradient(to bottom, rgba(255,0,0,0.8), rgba(255,255,255,0));
background-position: top left, center; background-position: top left, center;
@ -209,7 +209,7 @@ body {
min-height: 7.6875rem; min-height: 7.6875rem;
outline: none; outline: none;
} }
.intro-button__link::before { .intro-button-link::before {
content: ''; content: '';
position: absolute; position: absolute;
top: -13.125rem; top: -13.125rem;
@ -221,7 +221,7 @@ body {
transition: transform 0.3s ease-in-out; transition: transform 0.3s ease-in-out;
z-index: 5; z-index: 5;
} }
.intro-button__text { .intro-button-text {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
@ -237,18 +237,18 @@ body {
line-height: 130.023%; line-height: 130.023%;
letter-spacing: 0.0075rem; letter-spacing: 0.0075rem;
} }
.intro-button__text strong { .intro-button-text strong {
font-size: 2.625rem; font-size: 2.625rem;
font-weight: 600; font-weight: 600;
line-height: 130.023%; line-height: 130.023%;
letter-spacing: 0.013125rem; letter-spacing: 0.013125rem;
} }
.intro-button__link span { .intro-button-link span {
position: absolute; position: absolute;
display: block; display: block;
pointer-events: none; pointer-events: none;
} }
.intro-button__link span:nth-child(1) { .intro-button-link span:nth-child(1) {
height: 8px; height: 8px;
width: 100%; width: 100%;
top: 0; top: 0;
@ -268,7 +268,7 @@ body {
transform: translateX(100%); transform: translateX(100%);
} }
} }
.intro-button__link span:nth-child(2) { .intro-button-link span:nth-child(2) {
width: 8px; width: 8px;
height: 100%; height: 100%;
top: 0; top: 0;
@ -288,7 +288,7 @@ body {
transform: translateY(100%); transform: translateY(100%);
} }
} }
.intro-button__link span:nth-child(3) { .intro-button-link span:nth-child(3) {
height: 8px; height: 8px;
width: 100%; width: 100%;
bottom: 0; bottom: 0;
@ -308,19 +308,19 @@ body {
transform: translateX(-100%); transform: translateX(-100%);
} }
} }
.intro-button__link:hover span { .intro-button-link:hover span {
animation-play-state: paused; animation-play-state: paused;
} }
@media (width <= 48rem) { @media (width <= 48rem) {
.intro-header { .intro-header {
padding-bottom: 9rem;; padding-bottom: 9rem;;
} }
.intro-button__link { .intro-button-link {
height: 6.25rem; height: 6.25rem;
min-width: auto; min-width: auto;
border-radius: 0; border-radius: 0;
} }
.intro-button__text { .intro-button-text {
display: inline; display: inline;
padding-top: .5rem; padding-top: .5rem;
} }
@ -334,7 +334,7 @@ body {
max-width: 29.375rem; max-width: 29.375rem;
margin-bottom: 0; margin-bottom: 0;
} }
.intro-button__link:hover { .intro-button-link:hover {
transition: all .5s; transition: all .5s;
transform: rotate(-3deg) scale(1.125); transform: rotate(-3deg) scale(1.125);
} }
@ -352,18 +352,18 @@ body {
background: #fff; background: #fff;
position: relative; position: relative;
} }
.intro-text__children { .intro-text-body {
padding: 2.5rem 1.063rem 0.75rem; padding: 2.5rem 1.063rem 0.75rem;
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
} }
.intro-text__children p { .intro-text-lead {
width: 100%; width: 100%;
font-size: 1.5rem; font-size: 1.5rem;
margin: 0 0 1.5rem; margin: 0 0 1.5rem;
} }
@media (width > 48rem) { @media (width > 48rem) {
.intro-button + .intro-text__children { .intro-button + .intro-text-body {
padding-top: 7rem; padding-top: 7rem;
} }
} }
@ -372,7 +372,7 @@ body {
padding-bottom: 9rem;; padding-bottom: 9rem;;
} }
.intro-text, .intro-text,
.intro-text__children { .intro-text-body {
border-radius: 0.75rem; border-radius: 0.75rem;
} }
.intro-text { .intro-text {
@ -380,19 +380,19 @@ body {
max-width: 60rem; max-width: 60rem;
margin: 0 auto 6rem; margin: 0 auto 6rem;
} }
.intro-text__children { .intro-text-body {
padding-left: 4.5rem; padding-left: 4.5rem;
padding-right: 4.5rem; padding-right: 4.5rem;
} }
} }
.intro-text__children .block { .intro-text-body .block {
position: relative; position: relative;
} }
.intro-text__children .block__title { .intro-text-body .block__title {
margin: 1em 0 .8em; margin: 1em 0 .8em;
} }
.intro-text__children .block__title span { .intro-text-body .block__title span {
display: inline-block; display: inline-block;
padding: 10px 30px 14px; padding: 10px 30px 14px;
margin: 30px 20px 0; margin: 30px 20px 0;
@ -403,7 +403,7 @@ body {
border-color: orangered; border-color: orangered;
transform: rotate(-3deg) translateY(-25%); transform: rotate(-3deg) translateY(-25%);
} }
.intro-text__children .block__title:before { .intro-text-body .block__title:before {
content: ""; content: "";
height: 5px; height: 5px;
position: absolute; position: absolute;
@ -416,7 +416,7 @@ body {
transform: rotate(2deg) translateY(-50%); transform: rotate(2deg) translateY(-50%);
transform-origin: top left; transform-origin: top left;
} }
.intro-text__children .block__title:after { .intro-text-body .block__title:after {
content: ""; content: "";
height: 120%; height: 120%;
position: absolute; position: absolute;
@ -427,22 +427,22 @@ body {
background: var(--intro-bg-block-1); background: var(--intro-bg-block-1);
transform: rotate(2deg); transform: rotate(2deg);
} }
.intro-text__children .block:nth-of-type(6n+1) .block__title:after { .intro-text-body .block:nth-of-type(6n+1) .block__title:after {
background: var(--intro-bg-block-1); background: var(--intro-bg-block-1);
} }
.intro-text__children .block:nth-of-type(6n+2) .block__title:after { .intro-text-body .block:nth-of-type(6n+2) .block__title:after {
background: var(--intro-bg-block-2); background: var(--intro-bg-block-2);
} }
.intro-text__children .block:nth-of-type(6n+3) .block__title:after { .intro-text-body .block:nth-of-type(6n+3) .block__title:after {
background: var(--intro-bg-block-3); background: var(--intro-bg-block-3);
} }
.intro-text__children .block:nth-of-type(6n+4) .block__title:after { .intro-text-body .block:nth-of-type(6n+4) .block__title:after {
background: var(--intro-bg-block-4); background: var(--intro-bg-block-4);
} }
.intro-text__children .block:nth-of-type(6n+5) .block__title:after { .intro-text-body .block:nth-of-type(6n+5) .block__title:after {
background: var(--intro-bg-block-5); background: var(--intro-bg-block-5);
} }
.intro-text__children .block:nth-of-type(6n+6) .block__title:after { .intro-text-body .block:nth-of-type(6n+6) .block__title:after {
background: var(--intro-bg-block-6); background: var(--intro-bg-block-6);
} }
@ -465,7 +465,7 @@ body {
padding-bottom: 2rem; padding-bottom: 2rem;
} }
.intro-footer__body { .intro-footer-body {
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
@ -473,33 +473,33 @@ body {
padding: 0 10.625rem; padding: 0 10.625rem;
max-width: 80rem; max-width: 80rem;
} }
.intro-footer__body a:visited { .intro-footer-body a:visited {
color: var(--intro-color-gray); color: var(--intro-color-gray);
} }
.intro-footer__logo, .intro-footer-logo,
.intro-footer__links { .intro-footer-links {
display: flex; display: flex;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
} }
.intro-footer__logo { .intro-footer-logo {
max-height: 12.625rem; max-height: 12.625rem;
} }
.intro-footer__logo svg { .intro-footer-logo svg {
width: 100%; width: 100%;
} }
.intro-footer__links { .intro-footer-links {
gap: 1.875rem; gap: 1.875rem;
flex-wrap: wrap; flex-wrap: wrap;
margin-top: 2rem; margin-top: 2rem;
} }
@media (width <= 48rem) { @media (width <= 48rem) {
.intro-footer__logo { .intro-footer-logo {
display: none; display: none;
} }
} }
@media (width <= 64rem) { @media (width <= 64rem) {
.intro-footer__body { .intro-footer-body {
padding: 0 1rem; padding: 0 1rem;
} }
} }