# Aura UI
Interfaces nativas aceleradas por GPU para **macOS, Windows, Linux, Android e iOS** con un solo código en Rust.
Estilo encadenado con los nombres de Tailwind, componentes tipo shadcn, sombras por capas y animaciones, pensado para
gastar poco: poca RAM, casi nada de CPU en reposo y binarios pequeños.
```bash
cargo add aura-gui
```
Un solo crate con un módulo por capa: `aura_ui::core` (entidades, tareas, geometría, color, animación y capa de
sistema), `::gpu` (renderizador), `::view` (elementos, estilo, layout), `::widgets` (componentes), `::app` (ventana
y bucle) y `::ai` (agentes de IA; feature `ai`, activa por defecto: `default-features = false` la quita).
```rust
use aura_ui::prelude::*;
struct Contador { clics: u32 }
impl Render for Contador {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement {
div()
.size_full().flex_col().items_center().justify_center().gap_4()
.bg(Token::Canvas).text_color(Token::Text)
.child(div().p_6().rounded_lg().bg(Token::ElevatedSurface).shadow_md().child(format!("Clics: {}", self.clics)))
.child(Button::new("Sumar").variant(ButtonVariant::Primary).on_click(cx.click(|this, _| this.clics += 1)))
.into_element()
}
}
// Genera `main` en escritorio e iOS y `android_main` en Android.
aura_ui::main!(AppOptions::new("Contador"), |window| window.set_root(Contador { clics: 0 }));
```
## Tailwind
Los nombres son los de Tailwind en `snake_case` y todos son métodos de `Styled`: con `use aura_ui::prelude::*;` basta.
Cada uno solo fija un campo; no hay trabajo en tiempo de ejecución ni memoria extra hasta que se usan.
| Espaciado y tamaño (escala completa, 1 = 4 px) | `p_4 px_2 pt_0_5 m_8 mx_auto gap_x_2 w_64 h_1_2 size_10 min_w_0 max_w_full inset_0 top_px` |
| Flex | `flex_row flex_col_reverse flex_wrap grow shrink_0 flex_1 flex_auto items_baseline justify_evenly self_end content_between` |
| Cuadrícula | `grid_cols_3 grid_rows_2 col_span_2 col_start_2 row_span_3 grid_flow_col` + `gap_*` |
| Color | `blue(500) slate(900) rose(50)` del módulo `palette`, con `.with_alpha(0.5)`; degradados `bg_gradient_to_r(a, b)` |
| Texto | `text_xs … text_9xl font_thin … font_black text_center leading_tight tracking_wide truncate line_clamp_3 uppercase line_through font_mono` |
| Bordes | `border border_t_2 border_x_0 rounded_lg rounded_t_xl rounded_bl_full ring_2 ring_color(..)` |
| Efectos | `shadow_md glow opacity_50` |
| Transformaciones | `rotate_45 neg_rotate_12 scale_105 scale_x_50 translate_x_4 neg_translate_y_1_2 skew_x_6` (giran y escalan desde el centro; no cambian el layout) |
| Filtros | `blur_sm blur_md backdrop_blur_xl backdrop_blur_px(6.0)` |
| Apilado | `z_10 z_50 neg_z_10 z(7)` entre hermanos, también para el ratón |
| Overflow | `overflow_hidden overflow_x_scroll overflow_auto` |
| Estados | `hover active focus group_hover drag_over` |
| Variantes | `sm md lg xl xxl` (según el ancho de la ventana, primero móvil), `dark light` |
```rust
div()
.flex_col().gap_4().p_6().rounded_xl().border().border_color(palette::slate(200))
.md(|s| s.flex_row().items_center())
.dark(|s| s.bg(palette::slate(900)).border_color(palette::slate(700)))
.child(text("Título").text_2xl().font_semibold().tracking_tight().truncate())
.child(div().w_1_3().h_10().rounded_lg().bg_gradient_to_r(palette::blue(500), palette::indigo(600)))
```
**Matices**
- `backdrop_blur_*` difumina lo que hay detrás del elemento (cristal esmerilado). `blur_*` difumina el elemento y sus
hijos, pero se aplica sobre toda su caja: también suaviza el fondo que tenga debajo y no se extiende más allá del borde.
- Un desenfoque dentro de un elemento girado usa el rectángulo que envuelve al giro (no hay desenfoque girado).
- Sin GPU (el rasterizador por CPU) las transformaciones sin giro funcionan; un giro o una inclinación se pinta sin
girar. Las hitboxes de un elemento transformado son el rectángulo que envuelve a la figura.
- Un desenfoque pinta el frame en una textura propia y suelta su memoria al rato de no usarse.
**Todavía no existe**: `divide-*` y `space-*`, `cursor-*` más allá de los de `CursorStyle`, `select-none`,
`pointer-events-none`, `object-fit`, `mix-blend`, `sticky`, otros filtros (`brightness`, `grayscale`…), valores
arbitrarios (`w-[137px]`; usa `.w(px(137.0))`) y las variantes `disabled:` y `focus-within:`.
## Sombras y animaciones
```rust
div().shadow_lg() // presets xs, sm, md, lg, xl, 2xl
div().shadow(Shadows::one(BoxShadow::new(0., 4., 6.).spread(-1.)).with(BoxShadow::new(0., 12., 24.))) // capas
div().glow(Token::Accent, 16.0) // resplandor de color
let x = cx.transition("panel", if abierto { 180.0 } else { 0.0 }, Animation::new(ms(600)).easing(Easing::SPRING));
let t = cx.animate("pulso", Animation::new(ms(900)).ping_pong());
let c = cx.animate_between("fondo", Animation::new(ms(500)).easing(Easing::STANDARD), rojo, azul);
```
Curvas: `Linear`, `EaseIn`, `EaseOut`, `EaseInOut`, `CubicBezier(..)` (como CSS) y `Spring { bounce }`. Con retraso,
repetición y ida y vuelta. Una animación solo pide frames mientras corre.
## Eficiencia
Medido en macOS arm64 con el ejemplo `contador`, build release:
| Memoria (huella) | 25 MB |
| CPU en reposo | 0,00 s en 20 s |
| Binario | 7,0 MB (macOS) · 6,1 MB (iOS) · **4,2 MB** (iOS con `opt-level = "s"` y LTO completo) |
Cómo lo logra: el bucle duerme hasta que hay entrada, un temporizador o una animación; no pinta si no se ve; usa la GPU
de bajo consumo con un solo frame en cola; suelta cachés al pasar a segundo plano o ante un aviso de poca memoria; las
sombras son una sola primitiva analítica en el shader, sin pasadas de desenfoque.
Perfil recomendado para tus apps móviles:
```toml
[profile.release]
opt-level = "s"
lto = "fat"
codegen-units = 1
panic = "abort"
strip = "symbols"
```
## Menos código repetido
Todo viene de `use aura_ui::prelude::*;`, sin importar nada más.
```rust
// Clics: sin `|this: &mut V, _: &ClickEvent, cx|` ni `cx.notify()` (se avisa solo)
.on_click(cx.click(|this, cx| this.pick(name.clone(), cx)))
.on_click(cx.toggle(|t| &mut t.menu_open)) // invierte un bool
.on_click(cx.set(|t| &mut t.tab, Tab::Audio)) // asigna un valor
.on_click(cx.fire(Closed)) // emite un evento a quien se suscriba
// Segundo plano: sin hilo propio, canal ni `sender.update`; vuelve a la vista y redibuja
cx.job_blocking("login", |cancel| wait_for_browser(cancel), |this, ok, cx| this.on_login(ok, cx));
// Configuración: JSON en ~/.config/<app> (APPDATA en Windows), escritura atómica
let store = Store::new("miapp", "prefs.json").unwrap();
let theme = store.load().str_or("theme", "Aura Dark").to_owned();
// Controles y animación que toda app acaba copiando
div().pressable() // mano + hover + active
tool_button(icon, "Deshacer", enabled, cx.click(|t, _| t.undo()))
chip("Alta", selected) // etiqueta pulsable
switch(on, "notifs", cx.toggle(|t| &mut t.notifs))
let t = cx.phase("carga", motion); // segundos; `wave(t, 2.0, 0.0)` da una onda de 0 a 1
let a = cx.fade_in("panel", 200, motion);
```
Por qué cuesta poco: `cx.job` reutiliza el pool de la app (en vez de un hilo del sistema por tarea) y un trabajo
cancelado no llega a tocar la UI; guardar es un temporal más un renombrado; nada de esto despierta el bucle
cuando no hay trabajo.
## Móvil
Ver `docs/movil.md` del repositorio. Resumen: iOS compila para `aarch64-apple-ios` y el simulador; Android
para `aarch64-linux-android` con NativeActivity. Toque, deslizar con inercia, pulsación larga, teclado del sistema y
área segura están implementados. En iOS, añade el manifiesto UIScene al `Info.plist` (plantilla en `docs/ios/Info.plist`;
lo exige el SDK de iOS 27). Probado en el simulador de iPhone; **aún no en un iPhone ni un Android físicos.**
## Licencia
Aura UI tiene **doble licencia**, a tu elección (crates.io muestra la primera; la segunda va incluida en el paquete):
- **AGPL-3.0-only** (`LICENSE-AGPL`, <https://www.gnu.org/licenses/agpl-3.0.html>): gratis. Si la usas, tu app completa
debe publicarse bajo AGPL-3.0 con su código fuente.
- **Licencia comercial** (`LICENSE-COMMERCIAL.md`): para apps de código cerrado. Regalías del **10 % de los ingresos
brutos** de cada producto que use Aura UI. Para obtenerla escribe a <camiloghs1@gmail.com>.
Si no tienes la licencia comercial, aplica la AGPL-3.0. Los iconos de `src/widgets/svg` son de Lucide (ISC), con su
aviso en `src/widgets/svg/LICENSE-lucide`.
Copyright (C) 2026 Camilo Grajales Holguín.