Un documento vivo. Recoge las calibraciones, el ritmo y los acuerdos sobre la interfaz, y se actualiza cada vez que algo queda decidido.
Los iconos Material de androidx.compose.material.icons.outlined.* comparten un tamaño nominal —una rejilla de 24×24— pero la parte dibujada difiere de uno a otro. Dales a todos un simple Modifier.size(24.dp)y unos se verán grandes y pesados (Bolt, Delete) mientras otros parecerán diminutos (Contacts, VpnKey). Hay que calibrarlos a mano.
new_size = 24 × 52 / measured_px, acotado a [18..28].52 px es el glifo de referencia, Fingerprint; todo se mide contra él como unidad.
| Icon | Tamaño | Why |
|---|---|---|
| Add, Apps, ArrowBack, Close | 28dp | líneas finas, glifo de 36–41 px |
| AutoMode, Bolt, DeleteOutline, Edit, List, Photo, QrCode, Tune, DriveFileRenameOutline | 26dp | densidad media, glifo de unos 47 px |
| LocationOn | 24dp | la chincheta del mapa: no tocar, es una referencia |
| LocationOff | 26dp | la chincheta tachada: no tocar |
| ContentCopy, MyLocation, PeopleAlt, Wifi, ExpandMore, ExpandLess | 22dp | anchos o altos, y visualmente pesados |
| Contacts, VpnKey | 20dp | sobrecargados de detalle interno |
Se usa allí donde antes estaba Bolt: el botón de Automatización en la tarjeta de la clave, el icono de la sección en los ajustes. Tamaño 26 dp, igual que Bolt.
IconsDebugSheet.kt — el mapa iconSizeByName. La pantalla de depuración se abre desde SettingsScreen con el botón Iconos UI (depuración): arriba una FlowRow de iconos calibrados y abajo una lista de nombres, tamaños y contextos.
Usamos los roles estándar de Material 3, limitados a propósito a cuatro:
| Role | Tamaño / interlineado | Dónde se usa |
|---|---|---|
titleLarge SemiBold | 22sp/28sp | El título de la hoja inferior (Para el invitado · <objeto>) |
titleMedium SemiBold | 16sp/24sp | Encabezados de regla (Geolocalización, Wi-Fi, Intervalo, Activación) y etiquetas de FieldSection (Coordenadas, Radio, Modo, Redes guardadas) |
bodyMedium | 14sp/20sp | Rótulos de los botones de opción, el texto dentro de los botones píldora, nombres de los segmentos |
bodySmall muted | 12sp/16sp | Estados y pistas (36540 m hasta el punto, 74 m · máximo, el invitado puede reducirlo, textos de ayuda). Color: onSurfaceVariant |
Acordamos trabajar en múltiplos de 2 / 4 / 8 / 10 / 14 / 16 / 20. Nada de festín o hambruna: los elementos iguales reciben espacios iguales.
spacedBy(12.dp). Las tarjetas quedan a 12 dp entre sí.padding(horizontal=20.dp).padding(top=16.dp).verticalArrangement = spacedBy(16.dp) entre los bloques grandes.spacedBy(2.dp). Solo 2 dp entre la etiqueta en negrita y el estado o la pista debajo, para que se lean como un par de título y pie.Spacer(10.dp). Lo mismo en Geo, la conexión Wi-Fi y la lista de Wi-Fi.Un deslizador de Material 3 ocupa unos 48 dp de maquetación por el área táctil mínima, de los cuales la pista visible son solo unos 4 dp en el centro, con unos 22 dp de zona táctil vacía arriba y abajo. De ahí que:
Modifier.offset(y=-16.dp) para acercar el mango al texto.Modifier.sliderTrim() (un ayudante en GuestAutomationEditor.kt), que informa al padre de una altura de 22 dp en vez de 48 dp. Así, el padding(16.dp) da el mismo hueco del mango al borde inferior que en las tarjetas que acaban en un botón píldora.Cada regla —Geolocalización, Wi-Fi, Tiempo, Autorización, Confirmación— y el bloque de Activación toman uno de tres niveles. El círculo del icono de la tarjeta toma el color correspondiente cuando el interruptor está encendido.
| Key | Label | Colour | Description |
|---|---|---|---|
own |
Decide el invitado | #3BA55D (green) | El invitado elige él mismo el ajuste |
suggest |
Preset | #E5B409 (yellow) | El valor está fijado y el invitado puede cambiarlo |
force |
Locked | #CC3322 (red) | El valor está fijado y el invitado no puede cambiarlo |
Con el interruptor apagado el círculo se vuelve gris (#9AA0A6), sea cual sea el nivel elegido.
Un único hueco de aterrizaje de 38 dp en toda la aplicación:
CircleShape, con el fondo del color del nivel.contentAlignment = Alignment.Center.Envolverlos en Box(Modifier.size(38.dp), contentAlignment=Center) { RadioButton(...) } para que el centro del círculo del botón coincida con el centro del círculo de color de arriba. Sin la envoltura el botón se desplaza en horizontal, porque RadioButton se dibuja con sus propios rellenos.
Entre el botón de opción y su texto: Spacer(14.dp). Entre filas de botones de opción de un grupo: Column(spacedBy(4.dp)).
El área táctil mínima está desactivada: CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) — si no, cada fila de opción se hincha hasta 48 dp y rompe el ritmo.
Un interruptor estándar de Material 3, a la derecha del título de la tarjeta. Apaga la regla entera y entonces no se muestra el color del nivel: el círculo queda gris.
Un control propio para cambiar de modo dentro de una regla, hoy solo en Wi-Fi: Por una red concreta / Por las redes de alrededor. Dos elementos en una Row, cada uno con weight(1f):
colorScheme.surface, RoundedCornerShape(10.dp), relleno 4dp.primaryContainer, RoundedCornerShape(8.dp), con texto SemiBold.Un contorno, un icono y texto, del todo redondeados. Para acciones secundarias: Usar la posición actual, Capturar las redes de alrededor. Cuando están desactivados, el contorno y el texto bajan a alpha=0.4.
Véase la sección 3 sobre offset(y=-16.dp) y sliderTrim(). Antes de un deslizador siempre va un StatusLine con texto de estado (bodySmall, atenuado): 74 m · máximo, el invitado puede reducirlo.
Se abre tocando la fila de título de la tarjeta: toda la Row salvo el interruptor. Aparece justo bajo la cabecera, a todo el ancho del contenido de la tarjeta. Para eso:
Box con onGloballyPositioned, y medimos su anchura en dp.DropdownMenuItem con el modificador Modifier.width(anchorWidthDp).Los elementos del desplegable respiran en vez de ser filas planas: un marcador de color a la izquierda, la etiqueta en negrita y debajo la descripción en letra pequeña y atenuada. Se alinean en vertical por la primera línea, la etiqueta.
A diferencia de los círculos de color normales de las reglas, el círculo de la cabecera de Activación late.
drawBehind + Stroke(width=6dp).LinearEasing. No FastOutSlowInEasing — si no, al reiniciarse el ciclo se ve un tirón.sin(π·p) × 0.6: en p=0 y p=1 la opacidad es 0, con el pico en el medio. El reinicio es invisible.Una sola frase en una cadena de la interfaz no lleva punto. Los puntos aparecen solo cuando hay dos frases o más, para separarlas. Vale para toda descripción, pista, estado, indicación emergente, encabezado y frase suelta.
En los estados del deslizador en modo amarillo (Predefinido) indicamos sin rodeos la dirección de la libertad:
En rojo (Bloqueado): 74 m · exactamente / 5 de 7 · exactamente.
Decide el invitado / Predefinido / Bloqueado han cuajado; no los cambiamos.
Si una regla tiene estado apagado, exprésalo con un interruptor y no con una opción de radio del tipo No hace falta / No / Apagado. Si no, el usuario tiene dos maneras de apagar lo mismo, y eso confunde.
Examples:
En el bloque de Coordenadas los iconos de copiar y borrar están a la derecha a la altura de ambas líneas (la etiqueta y el estado). Implementado como Row { Column(weight=1f) { label; status }; CopyIcon; DeleteIcon }. Los iconos se centran en vertical respecto al par completo.
El cuerpo se despliega solo cuando:
Animation: expandVertically(tween(220)) + fadeIn(tween(220)) al abrir, shrinkVertically(tween(180)) + fadeOut(tween(120)) al cerrar.
Al recibir una clave de invitado, solo si la aplicación está en pantalla y la clave es nueva y no repetida. Si no, la guardamos en silencio.
sin(π·p) × 0.6. Cualquier simplificación estropea el resultado.Última actualización: 18 de abril de 2026 · versión del documento 2.07