Skip to main content

cross-platform-components

Overview of the Crystal cross-platform UI component system

Jump to install

Source facts

Repository
amberframework/asset_pipeline
Last source activity
February 18, 2026 at 11:55
Detected SKILL.md language
English
Stars
5
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
cross-platform-components
description
Overview of the Crystal cross-platform UI component system
version
1.0
# Cross-Platform UI Component System ## What This System Is The cross-platform UI component system extends the `asset_pipeline` shard with native rendering for macOS (AppKit), iOS (UIKit), and Android (JNI/Views), alongside the existing web (HTML) output path. A single Crystal source tree defines a `UI::View` abstract class hierarchy that is rendered by platform-specific visitors selected at compile time via `flag?()`. Zero bytes of platform code leak across targets. **Core model:** App code builds a tree of `UI::View` objects. A compile-time-selected `PlatformRenderer` (a `PlatformVisitor` subclass) walks the tree and produces native UI. The web renderer delegates to the existing `Components::Elements` system; native renderers call through ObjC or JNI bridges. ## Layer Model ``` App Code (single Crystal source) | v UI::View (abstract class hierarchy, pointer-sized virtual dispatch) | v UI::PlatformVisitor (compile-time selected via flag?()) | +-- Web::Renderer -> Components::Elements (existing HTML system) +-- AppKit::Renderer -> NSView via ObjC bridge [flag?(:macos)] +-- UIKit::Renderer -> UIView via ObjC bridge [flag?(:ios)] +-- Android::Renderer -> Android Views via JNI bridge [flag?(:android)] ``` Layout is 100% delegated to each platform's native engine. There is no custom constraint solver or Flexbox implementation in Crystal. `VStack` maps to `NSStackView` / `UIStackView` / `LinearLayout` / `flex-column`; each platform handles its own geometry. ## The 9 Core View Types | View Type | Purpose | |-----------|---------| | `UI::Label` | Read-only text display with font, color, alignment, and line limit | | `UI::Button` | Tappable element with a text label and `on_tap` callback | | `UI::VStack` | Vertical stack layout arranging children top to bottom | | `UI::HStack` | Horizontal stack layout arranging children leading to trailing | | `UI::ZStack` | Z-axis overlay stack drawing children back to front | | `UI::Image` | Image display with content mode and optional tint | | `UI::TextField` | Editable single-line text input with change callback | | `UI::ScrollView` | Scrollable container for content exceeding visible bounds | | `UI::Spacer` | Flexible space that expands within a stack layout | All view types are classes (not structs) because container views hold `Array(View)` children, creating recursive type relationships that Crystal structs cannot represent. ## When to Use This System Use the cross-platform UI layer when: - Building apps that target **web + macOS + iOS + Android** from one Crystal codebase - You want **native UI** on each platform (not a web view wrapper) - Your UI can be expressed as a **declarative view tree** of the 9 core view types - You need **type-safe, compile-time checked** UI code with zero runtime overhead for unused platforms Do NOT use this when: - Your app only targets the web (use `Components::Elements` directly) - You need highly custom rendering (use platform-native code directly) - You need views not in the 9 core types (extend the system first, see platform-renderers skill) ## Quick Reference: Counter App Example ```crystal require "ui" class CounterApp @count = 0 def build_view : UI::View stack = UI::VStack.new(spacing: 16.0, alignment: UI::Alignment::Center) label = UI::Label.new("Count: #{@count}") label.font = UI::Font.new(size: 24.0, weight: :bold) stack << label button = UI::Button.new("Increment") { @count += 1; nil } stack << button stack end end ``` This single definition renders as: - **Web:** `<div style="display:flex;flex-direction:column;gap:16px">` with `<span>` and `<button>` children - **macOS:** `NSStackView(vertical)` containing `NSTextField` (non-editable) and `NSButton` - **iOS:** `UIStackView(.vertical)` containing `UILabel` and `UIButton` - **Android:** `LinearLayout(VERTICAL)` containing `TextView` and `MaterialButton` ## Compile-Time Platform Selection The platform renderer is selected at compile time with zero runtime branching: ```crystal {% if flag?(:macos) %} require "./renderers/appkit_renderer" alias PlatformRenderer = UI::AppKit::Renderer {% elsif flag?(:ios) %} require "./renderers/uikit_renderer" alias PlatformRenderer = UI::UIKit::Renderer {% elsif flag?(:android) %} require "./renderers/android_renderer" alias PlatformRenderer = UI::Android::Renderer {% else %} require "./renderers/web_renderer" alias PlatformRenderer = UI::Web::Renderer {% end %} ``` The Crystal compiler eliminates all code for non-selected platforms. A macOS binary contains zero Android code, and vice versa. ## Key Source Locations | File | Purpose | |------|---------| | `src/ui/view.cr` | `UI::View` abstract class, `Color`, `Font`, `EdgeInsets`, enums | | `src/ui/views/*.cr` | All 9 concrete view types | | `src/ui/platform_visitor.cr` | `UI::PlatformVisitor` abstract class with 9 `visit` methods | | `src/ui.cr` | Top-level require that pulls in the full UI module | ## Related Skills - **component-api** -- Full API reference for all view types, value types, and patterns - **platform-renderers** -- How each renderer maps views to native elements - **glass-effects** -- Apple glass/translucency effect integration - **build-ui** -- Existing web component system (83 HTML elements, builder DSL)
View on GitHub