---
title: "Componente GlassBox — zenit Zig UI"
description: "Una superficie de vidrio con un desenfoque real del fondo. La legibilidad, el énfasis, hover / press y el muestreo del fondo pasan por una única ruta de…"
url: https://zenit.z.express/es/components/glassbox
language: es
alternate_en: https://zenit.z.express/components/glassbox.md
alternate_zh: https://zenit.z.express/zh/components/glassbox.md
alternate_ja: https://zenit.z.express/ja/components/glassbox.md
alternate_ko: https://zenit.z.express/ko/components/glassbox.md
alternate_fr: https://zenit.z.express/fr/components/glassbox.md
alternate_de: https://zenit.z.express/de/components/glassbox.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# GlassBox

`ui.widgets.GlassBox`

Una superficie de vidrio con un desenfoque real del fondo.

[Video](https://zenit.z.express/media/stories/glassbox.mp4?v=570227ca3d)

**BEHAVIOR CONTRACT**

La legibilidad, el énfasis, hover / press y el muestreo del fondo pasan por una única ruta de composición en Metal.

**RECORDED INTERACTION**

El harness pulsa la variante interactive sobre un fondo colorido.

`examples/storybook/stories.zig:1239`

```zig
pub fn buildGlassBox(scope: *ui.Scope, cx: *ui.Cx) anyerror!*ui.Node {
    const a = cx.allocator;
    const icons = ui.assets.common;
    const c = try col(cx, 20);

    // ── 1. Toolbar：分组玻璃 capsule（ToolbarSpacer 语义 = 两个独立 slab）──
    try c.appendChild(a, try label(cx, "Toolbar — grouped glass capsules (ToolbarSpacer splits slabs)"));
    {
        const bd = try glassBackdrop(cx, 130);
        const bar_row = try ui.box(cx, .{ .direction = .row, .gap = 12, .align_items = .center }, .{});
        const group1 = try glassCapsule(cx, 44, regularGlass());
        inline for (.{ icons.chevron_left, icons.chevron_right }) |asset| {
            try capsuleBody(group1).appendChild(a, try glassIconSlot(cx, asset));
        }
        const group2 = try glassCapsule(cx, 44, regularGlass());
        inline for (.{ icons.plus, icons.folder, icons.star }) |asset| {
            try capsuleBody(group2).appendChild(a, try glassIconSlot(cx, asset));
        }
        try bar_row.appendChild(a, group1);
        try bar_row.appendChild(a, group2);
        try bd.appendChild(a, bar_row);
        try c.appendChild(a, bd);
    }

    // ── 2. Tab bar：浮动 capsule + 独立 search 圆钮（Tab(role: .search)）──
    try c.appendChild(a, try label(cx, "Tab bar — floating capsule + detached search circle"));
    {
        const bd = try glassBackdrop(cx, 130);
        const bar_row = try ui.box(cx, .{ .direction = .row, .gap = 10, .align_items = .center }, .{});
        const tabbar = try glassCapsule(cx, 48, regularGlass());
        inline for (.{ "Today", "Library", "Radio" }, .{ icons.calendar, icons.folder_open, icons.scan }) |txt, asset| {
            const item = try ui.box(cx, .{ .direction = .row, .gap = 6, .align_items = .center, .padding = .{ .left = 10, .right = 10, .top = 0, .bottom = 0 } }, .{});
            var icon_style = ui.Style{};
            icon_style.width = .{ .px = 15 };
            icon_style.height = .{ .px = 15 };
            try item.appendChild(a, try ui.iconTint(cx, asset, ui.Color.rgba(255, 255, 255, 235), icon_style));
            try item.appendChild(a, try glassLabel(cx, txt, 550));
            try capsuleBody(tabbar).appendChild(a, item);
        }
        const search = try glassCapsule(cx, 48, regularGlass());
        try capsuleBody(search).appendChild(a, try glassIconSlot(cx, icons.search));
        try bar_row.appendChild(a, tabbar);
        try bar_row.appendChild(a, search);
        try bd.appendChild(a, bar_row);
        try c.appendChild(a, bd);
    }

    // ── 3. Buttons：.glass 与 .glassProminent（prominent = tinted glass）──
    try c.appendChild(a, try label(cx, "Buttons — .glass and .glassProminent (tinted)"));
    {
        const bd = try glassBackdrop(cx, 120);
        const btn_row = try ui.box(cx, .{ .direction = .row, .gap = 14, .align_items = .center }, .{});
        const plain = try glassCapsule(cx, 40, regularGlass());
        try capsuleBody(plain).appendChild(a, try glassLabel(cx, "Continue", 600));
        var prominent_params = regularGlass();
        prominent_params.glass_tint = ui.Color.rgba(0, 122, 255, 150);
        const prominent = try glassCapsule(cx, 40, prominent_params);
        capsuleBody(prominent).setBackgroundRaw(ui.Color.rgba(0, 122, 255, 90));
        try capsuleBody(prominent).appendChild(a, try glassLabel(cx, "Get", 650));
        var orange_params = regularGlass();
        orange_params.glass_tint = ui.Color.rgba(255, 149, 0, 150);
        const orange = try glassCapsule(cx, 40, orange_params);
        capsuleBody(orange).setBackgroundRaw(ui.Color.rgba(255, 149, 0, 90));
        try capsuleBody(orange).appendChild(a, try glassLabel(cx, "Tinted", 650));
        try btn_row.appendChild(a, plain);
        try btn_row.appendChild(a, prominent);
        try btn_row.appendChild(a, orange);
        try bd.appendChild(a, btn_row);
        try c.appendChild(a, bd);
    }

    // ── 4. GlassBox 面板：regular vs clear 变体 ──
    try c.appendChild(a, try label(cx, "GlassBox panel — .regular vs .clear (clear over 35% dimmed media)"));
    {
        const bd = try glassBackdrop(cx, 220);
        const pair = try ui.box(cx, .{ .direction = .row, .gap = 20, .align_items = .center }, .{});

        const regular_res = try W.GlassBox(.{ .title = "Regular", .subtitle = "adaptive, legible", .width = 240 }).mountBody(scope, cx);
        try regular_res.body.appendChild(a, try glassLabel(cx, "Functional layer content.", 450));
        try pair.appendChild(a, regular_res.box);

        // interactive：hover 提亮高光，press 玻璃增厚 + 微放大
        const inter_res = try W.GlassBox(.{ .title = "Interactive", .subtitle = "hover / press me", .width = 240, .interactive = true }).mountBody(scope, cx);
        try inter_res.body.appendChild(a, try glassLabel(cx, "Glass responds to touch.", 450));
        inter_res.box.meta.ownership.meta.test_id = "story.glassbox.interactive";
        try pair.appendChild(a, inter_res.box);

        // clear 变体：HIG 要求亮媒体上先叠 35% 黑色 dimming 层
        const dimmed = try ui.box(cx, .{
            .direction = .column,
            .padding = ui.Padding.all(10),
            .background = ui.Color.rgba(0, 0, 0, 89), // 35% dimming
        }, .{});
        (try dimmed.style.ensureExtFallible(a)).corner_radius = ui.CornerRadius.uniform(22);
        const clear_res = try W.GlassBox(.{ .title = "Clear", .subtitle = "media-rich backdrop", .width = 240, .variant = .clear }).mountBody(scope, cx);
        try clear_res.body.appendChild(a, try glassLabel(cx, "Backdrop shows through.", 450));
        try dimmed.appendChild(a, clear_res.box);
        try pair.appendChild(a, dimmed);

        try bd.appendChild(a, pair);
        try c.appendChild(a, bd);
    }

    // ── 5. Interactive motion：hover 抬升 / press 下沉 / 弹性回弹 ──
    try c.appendChild(a, try label(cx, "Interactive motion — hover lift, press sink, elastic release (geometry + material)"));
    {
        const bd = try glassBackdrop(cx, 240);
        const tile_row = try ui.box(cx, .{ .direction = .row, .gap = 20, .align_items = .center }, .{});
        try tile_row.appendChild(a, try glassMotionTile(scope, cx, "Lift", "ease-out rise", null, "story.glassbox.motion.lift"));
        try tile_row.appendChild(a, try glassMotionTile(scope, cx, "Tinted", "prominent blue", ui.Color.rgba(0, 122, 255, 150), "story.glassbox.motion.tinted"));
        try tile_row.appendChild(a, try glassMotionTile(scope, cx, "Elastic", "springy release", ui.Color.rgba(255, 149, 0, 150), "story.glassbox.motion.elastic"));
        try bd.appendChild(a, tile_row);
        try c.appendChild(a, bd);
    }

    // ── 6. Draggable glass：拖动玻璃观察实时折射 ──
    try c.appendChild(a, try label(cx, "Drag the glass — live refraction over the canvas"));
    {
        const bd = try glassBackdrop(cx, 260);
        const drag_node = try glassCapsule(cx, 56, regularGlass());
        drag_node.meta.ownership.meta.test_id = "story.glassbox.drag";
        drag_node.style.cursor = .pointer;
        const body = capsuleBody(drag_node);
        var icon_style = ui.Style{};
        icon_style.width = .{ .px = 18 };
        icon_style.height = .{ .px = 18 };
        try body.appendChild(a, try ui.iconTint(cx, ui.assets.common.mark, ui.Color.rgba(255, 255, 255, 235), icon_style));
        try body.appendChild(a, try glassLabel(cx, "Drag me", 650));

        const st = try a.create(GlassDragState);
        st.* = .{ .cx = cx, .target = drag_node };
        try scope.registerResource(@ptrCast(st), struct {
            fn cleanup(ptr: *anyopaque, alloc: std.mem.Allocator) void {
                const state: *GlassDragState = @ptrCast(@alignCast(ptr));
                alloc.destroy(state);
            }
        }.cleanup);
        drag_node.behavior.events.event_context = @ptrCast(st);
        drag_node.behavior.events.on_event = glassDragHandler;

        try bd.appendChild(a, drag_node);
        try c.appendChild(a, bd);
    }

    // ── 7. Floating nav toolbar：选中项盖 liquid glass lens，可拖拽 + 悬停放大 ──
    try c.appendChild(a, try label(cx, "Floating nav — draggable magnifying lens over the active item"));
    {
        const page = try ui.box(cx, .{
            .width = .{ .grow = .{} },
            .height = .{ .px = 200 },
            .align_items = .center,
            .justify = .center,
            .background = ui.Color.rgba(236, 231, 222, 255),
        }, .{});
        (try page.style.ensureExtFallible(a)).corner_radius = ui.CornerRadius.uniform(24);

        // 白色浮动 nav bar
        const bar_wrapper = try ui.box(cx, .{ .direction = .row, .position = .relative }, .{});
        const bar = try ui.box(cx, .{
            .direction = .row,
            .align_items = .center,
            .padding = .{ .left = 28, .right = 28, .top = 18, .bottom = 18 },
            .gap = 40,
            .background = ui.Color.rgba(255, 255, 255, 255),
        }, .{});
        const bar_ext = try bar.style.ensureExtFallible(a);
        bar_ext.corner_radius = ui.CornerRadius.uniform(44);
        bar_ext.setShadow(.{ .color = ui.Color.rgba(40, 36, 30, 30), .blur = 24, .offset_y = 10 });
        try bar_wrapper.appendChild(a, bar);

        // 灰色高亮底座：bar 第一个子节点（absolute），天然垫在 items 下面
        const pill = try ui.box(cx, .{
            .width = .{ .px = NAV_LENS_REST_W },
            .height = .{ .px = NAV_LENS_REST_H },
            .position = .absolute,
        }, .{});
        const pill_ext = try pill.style.ensureExtFallible(a);
        pill_ext.corner_radius = ui.CornerRadius.uniform(NAV_LENS_REST_H / 2);
        pill_ext.inset = .{ .top = .{ .px = 3 } };
        pill.setBackgroundRaw(ui.Color.rgba(228, 228, 231, 255));
        try bar.appendChild(a, pill);

        const icons2 = ui.assets.common;
        var item_nodes: [NAV_ITEM_COUNT]*ui.Node = undefined;
        inline for (.{ "Dashboard", "Explore", "Futures", "Rewards", "Exchange" }, .{ icons2.mark, icons2.search, icons2.file_text, icons2.star, icons2.link }, 0..) |txt, asset, i| {
            const item = try ui.box(cx, .{ .direction = .column, .align_items = .center, .gap = 6 }, .{});
            var icon_style = ui.Style{};
            icon_style.width = .{ .px = 22 };
            icon_style.height = .{ .px = 22 };
            try item.appendChild(a, try ui.iconTint(cx, asset, ui.Color.rgba(116, 122, 134, 255), icon_style));
            try item.appendChild(a, try ui.text(cx, txt, .{ .font_size = 15, .font_weight = 600, .color = ui.Color.rgba(28, 30, 36, 255) }));
            try bar.appendChild(a, item);
            item_nodes[i] = item;
        }

        // lens：绝对定位盖在 bar 上，上下出血
        const lens = try ui.box(cx, .{
            .width = .{ .px = NAV_LENS_REST_W },
            .height = .{ .px = NAV_LENS_REST_H },
            .position = .absolute,
        }, .{});
        const lens_ext = try lens.style.ensureExtFallible(a);
        lens_ext.inset = .{ .top = .{ .px = -4 } };
        lens_ext.corner_radius = ui.CornerRadius.uniform(NAV_LENS_REST_H / 2);
        // 静止态：lens 不画任何东西（命中区），玻璃只在按下时出现
        lens.style.cursor = .pointer;
        lens.meta.ownership.meta.test_id = "story.glassbox.navlens";
        try bar_wrapper.appendChild(a, lens);

        const st = try a.create(NavLensState);
        st.* = .{ .cx = cx, .lens = lens, .pill = pill, .bar = bar, .container = bar_wrapper, .items = item_nodes };
        try scope.registerResource(@ptrCast(st), struct {
            fn cleanup(ptr: *anyopaque, alloc: std.mem.Allocator) void {
                const state: *NavLensState = @ptrCast(@alignCast(ptr));
                alloc.destroy(state);
            }
        }.cleanup);
        lens.behavior.events.event_context = @ptrCast(st);
        lens.behavior.events.on_event = navLensHandler;
        // snap 挂在 in-flow 容器上（absolute 节点的 before_render 不保证执行）
        bar_wrapper.behavior.events.event_context = @ptrCast(st);
        bar_wrapper.addBeforeRender(navLensBeforeRender);

        try page.appendChild(a, bar_wrapper);
        try c.appendChild(a, page);
    }

    return c;
}
```

[Ver el código en GitHubexamples/storybook/stories.zig:1239](https://github.com/version-next/zenit/blob/HEAD/examples/storybook/stories.zig#L1239)

El código literal de la story en el Storybook (zenit 5f9add5+wip 2026-09-30). `W` es `ui.widgets`; `col` / `row` / `label` son pequeños helpers de layout del Storybook.

### GlassBoxProps

`ui.widgets.GlassBox`

[src/ui/components/glass\_box/mod.zig:48](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/glass_box/mod.zig#L48)

```zig
fn GlassBox(props: GlassBoxProps) GlassBoxBuilder
```

| Campo | Tipo | Predeterminado | Descripción |
| --- | --- | --- | --- |
| `title` | `?[]const u8` | `null` | — |
| `subtitle` | `?[]const u8` | `null` | — |
| `width` | `?f32` | `null` | — |
| `height` | `?f32` | `null` | — |
| `padding` | `?Padding` | `null` | — |
| `body_gap` | `f32` | `12` | — |
| `radius` | `f32` | `18` | — |
| `readability` | `GlassBoxReadability` `.balanced` `.preferred` `.maximum` | `.preferred` | — |
| `emphasis` | `GlassBoxEmphasis` `.subtle` `.regular` `.prominent` | `.regular` | — |
| `variant` | `GlassBoxVariant` `.regular` `.clear` | `.regular` | — |
| `tint` | `?Color` | `null` | — |
| `interactive` | `bool` | `false` | Apple Liquid Glass interactive：hover 提亮高光、press 玻璃增厚 + 微放大。 |
| `scroll_edge` | `?ScrollEdgeBinding` | `null` | scroll-edge effect：绑定 ScrollArea 后，内容滚过玻璃边缘时出现渐进 模糊/压暗带（随滚动位置实时驱动 + 插值平滑）。 |

### \*Node

`ui.widgets.GlassBox`

mount devuelve `*Node` directamente: añádelo a un nodo padre.
