Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Lo que cambia para quien usa Alcancía, en palabras simples. Cada versión, con
*What changes for people using Alcancía, in plain words. Every version, with its download and SHA-256 checksum, is in Releases.*

## Sin publicar · Unreleased
- Alcancía tiene el estilo nuevo de la familia: en el computador, barra lateral oscura y fondo cálido. En el celular, la barra de abajo queda igual. · *Alcancía has the family's new look: on the computer, a dark sidebar and a warm background. On the phone, the bottom bar stays the same.*

## 0.2.0 · 2026-11-08

Expand Down
3 changes: 2 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"tema": [
"sistema",
"claro",
"oscuro"
"oscuro",
"vidrio"
]
},
"sin_acciones": [
Expand Down
12 changes: 6 additions & 6 deletions piezas.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@
}
},
"tema": {
"version": "0.2.1",
"version": "0.4.0",
"archivos": {
"ui/tema.css": "1d43c1cf027fafcbd282bd90bc87ae3937059f247fb823cd1aab2a26f7c9363e",
"ui/tema.js": "b679ecb7bc80556b70e3f3fe768f0e3af0086bdf44c0d73ed0867da6a8f33f6e"
"ui/tema.css": "0f92833b29256c23f533da70292b53c493eae2da2690b5324f71429ab0ce1c74",
"ui/tema.js": "d476607ea31535a91fd2a81e7eef81558bfbbfc03dd772b447d7b359f9913dd4"
},
"ficha": {
"requiere": [],
Expand All @@ -91,10 +91,10 @@
}
},
"componentes": {
"version": "0.3.0",
"version": "0.4.0",
"archivos": {
"ui/componentes.js": "310712e40f69b674f5d64fc723ccd2dbf2ad114f6ac68ee72a598cf4c36bb76a",
"ui/componentes.css": "101586f0fa7aa509f42b626de378c5ad32d176da5a4c0ad85b081a47908fe56f"
"ui/componentes.js": "fd5eafba3affb59be9506c57f38eff7a712297371621fe070b2b6d0ce3dfdf80",
"ui/componentes.css": "7ed1292d7ecb9fb6674a70808d5caca7611fa5be79578464b86ae32b4fff196a"
},
"ficha": {
"requiere": [
Expand Down
14 changes: 8 additions & 6 deletions ui/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
* Escala: cifra protagonista (44–52 px) · títulos (20 px) · texto (15 px) · detalle (13 px). Espacios en múltiplos de 4 y 8.
* Mobile first: la misma interfaz va a una app de celular; en escritorio la barra inferior pasa a ser una barra lateral.
* Los tokens (--fondo, --panel, --panel-2, --texto, --texto-suave, --tenue, --linea, --acento, --rojo, --verde…) son de la pieza
* «tema» (ui/tema.css): aquí solo van los colores de Alcancía como pares claro/oscuro. Botones, diálogos, avisos y filas de Ajustes
* «tema» (ui/tema.css), con los neutros de la familia: aquí solo van el naranjo, el rojo y el verde de Alcancía como pares. En
* escritorio la barra lateral es oscura, como en toda la familia (los tokens de adentro se resuelven en oscuro). Botones, diálogos, avisos y filas de Ajustes
* son de la pieza «componentes» (ui/componentes.css, clases pz-); aquí, lo que Alcancía les ajusta. */
:root {
--fondo-claro: #f5f4f2; --fondo-oscuro: #131211; --panel-claro: #ffffff; --panel-oscuro: #1c1a18; --panel-2-claro: #efedea; --panel-2-oscuro: #171615;
--linea-claro: #e5e2de; --linea-oscuro: #2d2a27; --texto-claro: #1c1a18; --texto-oscuro: #f2efeb; --texto-suave-claro: #625c56; --texto-suave-oscuro: #aaa39c;
--tenue-claro: #b3ada6; --tenue-oscuro: #57524d;
--acento-claro: #ad4f27; --acento-oscuro: #ec8d5e; --sobre-acento-oscuro: #111111;
--rojo-claro: #c0312a; --rojo-oscuro: #f2766e; --verde-claro: #1f7a49; --verde-oscuro: #55c38c;
--radio-chico: 10px; --barra-alto: 64px;
Expand Down Expand Up @@ -60,7 +58,11 @@ p { margin: 0; }
.app { display: grid; grid-template-columns: 224px minmax(0, 1fr); height: 100vh; }
.principal-area { overflow-y: auto; padding-bottom: 0; }
.contenido { padding: 36px 40px 64px; }
.lateral { position: static; display: flex; flex-direction: column; background: var(--panel-2); border-top: 0; border-right: 1px solid var(--linea); padding: 0 12px 16px; }
.lateral { position: static; display: flex; flex-direction: column; border-top: 0; padding: 0 12px 16px;
/* Oscura en los dos modos: lo mismo que .pz-zona-oscura de «componentes», solo en escritorio (en el celular, la barra inferior
sigue clara). */
color-scheme: dark; --fondo: var(--barra-lateral); --panel: var(--barra-lateral-2); --panel-2: var(--barra-lateral);
--texto: var(--barra-lateral-texto); --texto-suave: var(--barra-lateral-suave); background: var(--barra-lateral); color: var(--texto); }
.cabeza { display: flex; padding: 36px 8px 20px; -webkit-app-region: drag; }
.nav { display: flex; flex-direction: column; align-items: stretch; height: auto; gap: 2px; margin: 0; }
.nav-agregar { order: -1; width: 100%; height: 44px; border-radius: 10px; margin-bottom: 16px; display: flex; justify-content: center; gap: 8px; font-weight: 650; -webkit-app-region: no-drag; }
Expand All @@ -70,7 +72,7 @@ p { margin: 0; }
.nav-item .ico { height: auto; }
.nav-item .ico svg { width: 20px; height: 20px; }
.nav-nombre { font-size: 15px; font-weight: 600; }
.nav-item:hover { background: rgba(127, 120, 112, .1); color: var(--texto); }
.nav-item:hover { background: var(--barra-lateral-2); color: var(--texto); }
.nav-item[aria-current="page"] { background: var(--panel); box-shadow: inset 3px 0 0 var(--acento); }
.nav-item[aria-current="page"] .ico::after { display: none; }
.nav-item { position: relative; }
Expand Down
70 changes: 69 additions & 1 deletion ui/componentes.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/* Botones (el texto largo se parte en dos líneas antes de salirse a 390 px) */
.pz-boton { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: var(--pz-boton-alto); padding: 6px 14px;
border: 1px solid var(--linea); border-radius: var(--radio-chico); background: var(--panel); color: var(--texto); font: inherit;
line-height: 1.25; text-align: center; cursor: pointer; max-width: 100%; }
font-weight: 500; line-height: 1.25; text-align: center; cursor: pointer; max-width: 100%; transition: background-color .12s, border-color .12s; }
.pz-boton:hover { background: var(--hover); }
.pz-boton.pz-principal { background: var(--acento); border-color: var(--acento); color: var(--sobre-acento); font-weight: 600; }
.pz-boton.pz-principal:hover { background: color-mix(in srgb, var(--acento) 90%, var(--texto)); }
Expand Down Expand Up @@ -89,6 +89,74 @@
.pz-giro { width: 18px; height: 18px; flex: none; border: 2px solid var(--linea); border-top-color: var(--acento); border-radius: 50%; animation: pz-giro .8s linear infinite; }
@keyframes pz-giro { to { transform: rotate(360deg); } }

/* El marco de la app: barra lateral oscura + contenido. En el celular la barra queda arriba, en una fila: lo que no cabe ahí va con
.pz-solo-ancho (se oculta a 760 px o menos). Una app con barra inferior en el celular usa .pz-lateral solo en escritorio.
<div class="pz-marco"><nav class="pz-lateral">
<button class="pz-marca">logo + nombre</button> <button class="pz-boton pz-principal">Acción</button>
<p class="pz-lateral-titulo">Grupo</p> <button class="pz-lateral-enlace" aria-current="page">Sección</button>
<div class="pz-lateral-pie">…</div></nav>
<main class="pz-contenido">…</main></div> */
.pz-marco { display: flex; min-height: 100vh; min-height: 100dvh; }
/* Una zona oscura en los dos modos (la barra lateral, el panel oscuro, o la barra propia de una app con .pz-zona-oscura): con
color-scheme: dark, todos los tokens de «tema» de adentro se resuelven en oscuro (texto, líneas, acento, campos, botones), y
fondo y paneles pasan a los de la barra lateral. Lo de la app que va adentro se ve bien sin reglas propias. */
.pz-lateral, .pz-oscuro, .pz-zona-oscura { color-scheme: dark; --fondo: var(--barra-lateral); --panel: var(--barra-lateral-2);
--panel-2: var(--barra-lateral); --texto: var(--barra-lateral-texto); --texto-suave: var(--barra-lateral-suave);
--hover: color-mix(in srgb, var(--barra-lateral-texto) 9%, transparent); background: var(--barra-lateral); color: var(--texto); }
.pz-lateral { flex: none; width: var(--pz-lateral-ancho, 240px); display: flex; flex-direction: column; gap: 4px; padding: 18px 12px; overflow-y: auto; }
.pz-lateral :focus-visible { outline-color: var(--barra-lateral-acento); }
.pz-lateral .pz-boton:not(.pz-principal) { background: var(--barra-lateral-2); border-color: transparent; color: var(--barra-lateral-texto); }
.pz-lateral .pz-boton.pz-principal { background: var(--barra-lateral-acento); border-color: var(--barra-lateral-acento); color: var(--sobre-barra-lateral-acento); }
.pz-lateral > .pz-boton { margin: 8px 0 10px; }
.pz-marca { display: flex; align-items: center; gap: 10px; min-height: 40px; padding: 4px 8px; border: 0; background: none; color: inherit;
font: inherit; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; text-align: left; cursor: pointer; }
.pz-marca img, .pz-marca svg { width: 28px; height: 28px; border-radius: 7px; flex: none; }
.pz-lateral-titulo { margin: 14px 8px 4px; font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--barra-lateral-suave); }
.pz-lateral-enlace { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px; padding: 8px 10px; border: 0; border-radius: var(--radio-chico);
background: none; color: var(--barra-lateral-texto); font: inherit; font-size: 14px; text-align: left; text-decoration: none; cursor: pointer; }
.pz-lateral-enlace:hover, .pz-lateral-enlace[aria-current]:not([aria-current="false"]) { background: var(--barra-lateral-2); }
.pz-lateral-enlace[aria-current]:not([aria-current="false"]) { font-weight: 600; box-shadow: inset 3px 0 0 var(--barra-lateral-acento); }
.pz-lateral-enlace small { margin-left: auto; color: var(--barra-lateral-suave); font-size: 12px; font-variant-numeric: tabular-nums; }
.pz-lateral-pie { margin-top: auto; padding-top: 12px; display: grid; gap: 2px; }
.pz-contenido { flex: 1; min-width: 0; padding: 36px 40px 64px; }
:where(.pz-contenido) h1 { font-size: 30px; line-height: 1.15; margin: 0 0 6px; }
@media (max-width: 760px) {
.pz-marco { flex-direction: column; }
.pz-lateral { width: auto; flex-direction: row; flex-wrap: nowrap; align-items: center; gap: 8px; padding: 8px 12px; padding-top: calc(8px + env(safe-area-inset-top, 0px)); overflow: visible; }
.pz-lateral > .pz-boton { margin: 0; }
.pz-lateral-pie { margin: 0 0 0 auto; padding: 0; display: flex; }
.pz-solo-ancho, .pz-lateral-titulo { display: none !important; }
.pz-contenido { padding: 20px 16px 48px; }
:where(.pz-contenido) h1 { font-size: 26px; }
}

/* Superficies: tarjeta, cifras (una cifra grande con su etiqueta), panel oscuro (lo que pasa en vivo, lo que ahorras) */
.pz-tarjeta { padding: 16px; background: var(--panel); border: 1px solid var(--linea); border-radius: var(--radio); }
.pz-tarjetas { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); gap: 12px; padding: 0; margin: 12px 0; list-style: none; }
.pz-cifras { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr)); gap: 12px; margin: 16px 0; }
.pz-cifra { display: grid; gap: 2px; padding: 14px 16px; background: var(--panel); border: 1px solid var(--linea); border-radius: var(--radio); }
.pz-cifra b { font-size: 26px; line-height: 1.15; font-weight: 700; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.pz-cifra small { color: var(--texto-suave); font-size: 13px; }
.pz-oscuro { padding: 16px 18px; border-radius: var(--radio-grande); }
.pz-oscuro small, .pz-oscuro .pz-ayuda { color: var(--barra-lateral-suave); }
.pz-oscuro b { color: var(--barra-lateral-acento); }

/* Etapas: dónde va la persona en un proceso de varios pasos. <ol class="pz-etapas"><li class="pz-hecha"><span>1</span>Idea</li>
<li class="pz-actual" aria-current="step"><span>2</span>Plan</li><li><span>3</span>Listo</li></ol> */
.pz-etapas { display: flex; flex-wrap: wrap; gap: 4px; margin: 0 0 18px; padding: 0; list-style: none; }
.pz-etapas li { display: flex; align-items: center; gap: 8px; padding: 5px 12px 5px 5px; border-radius: 999px; font-size: 13px; color: var(--texto-suave); }
.pz-etapas li > span { width: 22px; height: 22px; display: grid; place-items: center; border-radius: 50%; background: var(--panel-2); font-size: 12px; font-weight: 600; }
.pz-etapas .pz-hecha { color: var(--verde); }
.pz-etapas .pz-hecha > span { background: var(--verde-suave); }
.pz-etapas .pz-actual { background: var(--texto); color: var(--sobre-texto); font-weight: 600; }
.pz-etapas .pz-actual > span { background: var(--acento); color: var(--sobre-acento); }

/* Dos columnas: lo principal y un panel al lado (abajo cuando no cabe) */
.pz-dos-columnas { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 24px; }
.pz-dos-columnas > :first-child { flex: 999 1 460px; min-width: 0; }
.pz-columna-lado { flex: 1 1 280px; max-width: 400px; display: grid; gap: 12px; }
@media (max-width: 760px) { .pz-columna-lado { max-width: none; } }

/* Avisos (toast): uno a la vez, abajo al centro. --pz-toast-abajo para dejar espacio a una barra inferior. Colores sólidos
(--texto/--sobre-texto, --rojo/--sobre-rojo): se leen aunque la app tenga --fondo o --panel transparentes. */
.pz-toast { position: fixed; left: 50%; bottom: calc(var(--pz-toast-abajo, 24px) + env(safe-area-inset-bottom, 0px)); z-index: 80;
Expand Down
16 changes: 15 additions & 1 deletion ui/componentes.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,22 @@
}
/** `actualizacionProgreso {paso: "1"|"2"|"3"|"error", codigo, texto, valores, total, pct?}`. */
function actualizacionProgreso(a = {}) {
const antes = act.estado;
act.estado = { paso: String(a.paso || ""), texto: String(a.texto || ""), valores: a.valores || {}, total: Number(a.total) || 3, pct: a.pct };
if (act.estado.paso === "error") aviso(act.estado.texto || "No se pudo actualizar", { valores: act.estado.valores, error: true });
if (act.estado.paso === "error") { aviso(act.estado.texto || "No se pudo actualizar", { valores: act.estado.valores, error: true }); pintar(); return; }
// Mismo paso, solo cambia el avance: se cambia la fila, no la ventana entera (repintarla cada medio segundo parpadea).
const filas = typeof document !== "undefined" ? document.querySelectorAll(".pz-act") : [];
if (antes && typeof antes === "object" && antes.paso === act.estado.paso && filas.length) {
const v = act.estado, n = Math.max(1, Math.min(v.total, parseInt(v.paso, 10) || 1));
const pct = typeof v.pct === "number" && Number.isFinite(v.pct) ? Math.max(0, Math.min(100, Math.round(v.pct))) : null;
const texto = (v.texto ? Tp(v.texto, v.valores) : "") + (pct !== null ? ` · ${pct}%` : "");
for (const f of filas) {
const b = f.querySelector("b"), barra = f.querySelector("progress");
if (b) b.textContent = texto;
if (barra) barra.value = (n - 1) * 100 + (pct ?? 0);
}
return;
}
pintar();
}
function filaActualizacion() {
Expand Down
3 changes: 2 additions & 1 deletion ui/idiomas/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,7 @@
"Ya puedes empezar. Todo lo que elegiste se cambia cuando quieras en {donde}.": "You're ready to go. You can change anything you chose at any time in {donde}.",
"Ya puedes empezar.": "You're ready to go.",
"Ocultar otras": "Hide Others",
"Abrir {app}": "Open {app}"
"Abrir {app}": "Open {app}",
"Vidrio": "Glass"
}
}
Loading
Loading