Compare commits

..

No commits in common. "bf1d9422237e6f9b1bf57685853a3dfd89bebffc" and "0cf9cebe14e63ae33574fe9986a27d96e21b390c" have entirely different histories.

5 changed files with 82 additions and 99 deletions

View file

@ -1,7 +1,7 @@
# 🔃 Dependencias
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.
* [Config](https://docs.rs/config) para cargar y procesar las opciones de configuración.
@ -26,15 +26,8 @@ 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*
# 🎨 CSS
# 🎨 Icono
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).
"La Criatura" 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
[ICONFINDER](https://www.iconfinder.com).

View file

@ -22,13 +22,12 @@ pub enum IntroOpening {
///
/// Usa la imagen de PageTop para mostrar:
///
/// - Una **figura decorativa** (que incluye la *mascota* de PageTop) antecediendo al contenido.
/// - Una **figura decorativa** (que incluye el *monster* de PageTop) antecediendo al contenido.
/// - 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 **á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
/// 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.
/// contenido libre para crear párrafos vistosos de texto. Aunque admite todo tipo de componentes.
///
/// # Ejemplos
///
@ -71,9 +70,7 @@ pub enum IntroOpening {
/// .with_title(L10n::l("intro_custom_block_title"))
/// .with_child(Html::with(move |cx| {
/// html! {
/// p class="intro-text-lead" {
/// (L10n::l("intro_custom_paragraph_1").using(cx))
/// }
/// p { (L10n::l("intro_custom_paragraph_1").using(cx)) }
/// p { (L10n::l("intro_custom_paragraph_2").using(cx)) }
/// }
/// })),
@ -138,41 +135,39 @@ impl Component for Intro {
Ok(html! {
div class="intro" {
div class="intro-header" {
section class="intro-header-body" {
h1 class="intro-header-title" {
section class="intro-header__body" {
h1 class="intro-header__title" {
span { (self.title().using(cx)) }
(self.slogan().using(cx))
}
}
aside class="intro-header-img" aria-hidden="true" {
div class="intro-header-mascot" {
aside class="intro-header__image" aria-hidden="true" {
div class="intro-header__monster" {
(PageTopSvg::Color.render(cx))
}
}
}
div class="intro-content" {
section class="intro-content-body" {
section class="intro-content__body" {
div class="intro-text" {
@if let Some((txt, lnk)) = self.button() {
div class="intro-button" {
a
class="intro-button-link"
class="intro-button__link"
href=((lnk)(cx))
target="_blank"
rel="noopener noreferrer"
{
span {} span {} span {}
div class="intro-button-text" {
div class="intro-button__text" {
(txt.using(cx))
}
}
}
}
div class="intro-text-body" {
div class="intro-text__children" {
@if *self.opening() == IntroOpening::PageTop {
p class="intro-text-lead" {
(L10n::l("intro_text1").using(cx))
}
p { (L10n::l("intro_text1").using(cx)) }
div id="intro-badges" {
img
src="https://img.shields.io/crates/v/pagetop.svg?label=PageTop&style=for-the-badge"
@ -187,9 +182,7 @@ impl Component for Intro {
))
alt=[L10n::l("intro_license_label").lookup(cx)] {}
}
p class="intro-text-lead" {
(L10n::l("intro_text2").using(cx))
}
p { (L10n::l("intro_text2").using(cx)) }
}
(self.children().render(cx))
}
@ -197,11 +190,11 @@ impl Component for Intro {
}
}
div class="intro-footer" {
section class="intro-footer-body" {
div class="intro-footer-logo" {
section class="intro-footer__body" {
div class="intro-footer__logo" {
(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://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)) }

View file

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

View file

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