Skip to content

ermanakar/LubaUI

Repository files navigation

LubaUI

Ship beautiful SwiftUI apps without thinking about design.

You're building fast. You're vibing with your AI. You don't want to stop and think about what shade of gray a subtitle should be, whether your button animation is too bouncy, or if your touch targets meet accessibility standards. You want to write LubaButton("Save") { save() } and have it just feel right.

That's LubaUI.

Swift Platforms License

Form patterns — signup, settings, payment flows   Theming — live theme switching with color and spacing overrides   Typography — SF Rounded with message thread and pricing card   Composability — mix and match primitives on any view   Loading patterns — skeletons, spinners, and progress


30-Second Demo

import SwiftUI
import LubaUI

struct ContentView: View {
    @State private var email = ""
    @State private var agreed = false

    var body: some View {
        VStack(spacing: LubaSpacing.lg) {
            Text("Create Account")
                .font(LubaTypography.title)
                .foregroundStyle(LubaColors.textPrimary)

            LubaTextField.email(text: $email)

            LubaCheckbox(isChecked: $agreed, label: "I agree to the terms")

            LubaButton("Get Started", style: .primary, isDisabled: !agreed) {
                createAccount()
            }
        }
        .padding(LubaSpacing.xl)
    }
}

No color codes. No animation curves. No magic numbers. It just looks and feels like a real app.


Installation

Swift Package Manager

Xcode: File → Add Package Dependencies → Enter:

https://github.com/ermanakar/LubaUI

Package.swift:

dependencies: [
    .package(url: "https://github.com/ermanakar/LubaUI", from: "0.1.0")
]

Why LubaUI

Built for AI-Assisted Development

Every token, every constant, every API in LubaUI is designed to be understood by your AI coding assistant. Semantic naming (LubaColors.textPrimary, not gray900), strict tokenization (no magic numbers), and documented rationale for every design decision. When you pair with an AI, it doesn't guess — it knows this system.

One Import, Whole Design Language

You get colors, typography, spacing, motion, haptics, accessibility, and 35+ components that all share the same DNA. Your settings screen looks like it belongs with your onboarding flow because they're built from the same tokens.

The Details Are Done

The hard stuff is already decided:

  • 0.97 press scale — not 0.98 (too subtle), not 0.95 (too cartoonish)
  • Spring animations tuned to feel alive, not mechanical
  • 4pt spacing grid for mathematical rhythm
  • 44pt touch targets for accessibility compliance
  • Adaptive colors that look right in light and dark mode

You don't have to make these decisions. They're already made, and they're good.


Primitives — Make Anything Interactive

This is the core idea. Behaviors aren't locked inside components — they're modifiers you can attach to any view.

.lubaPressable() — Tap anything

// A card that feels like a button
LubaCard {
    Text("Tap me")
}
.lubaPressable { print("Tapped!") }

// An image that responds to touch
Image("hero")
    .lubaPressable(scale: 0.98) { showDetail = true }

.lubaSwipeable() — Swipe actions on any row

// Swipe to delete
MessageRow()
    .lubaSwipeable(trailing: [.delete { removeItem() }])

// Multiple actions
MessageRow()
    .lubaSwipeable(
        leading: [.pin { pinMessage() }],
        trailing: [.archive { archive() }, .delete { delete() }]
    )

.lubaLongPressable() — Long press with progress

// Destructive action with visual confirmation
Image(systemName: "trash")
    .lubaLongPressable { confirmDeletion() }

// With progress ring
LubaCard { content }
    .lubaLongPressable(showProgress: true, duration: 1.0) {
        confirmDeletion()
    }

.lubaShimmerable() — Loading state for any view

// Shimmer anything while loading
Image("hero")
    .lubaShimmerable(isLoading: isLoading)

LubaExpandable — Accordion behavior

LubaExpandable(isExpanded: $isOpen) {
    Text("FAQ Question")
} content: {
    Text("The answer goes here")
}

Components

Interactive Controls

Component What It Does
LubaButton Primary, secondary, ghost, destructive, subtle. Loading states. Icons.
LubaTextField Labels, icons, error states. Convenience: .email(), .secure()
LubaTextArea Multi-line text editor with character counter and limits
LubaSearchBar Search input with cancel button and submit handler
LubaCheckbox Animated checkmark with label
LubaRadio Radio button groups
LubaToggle iOS-style toggle switch
LubaSlider Value slider with optional labels
LubaStepper Numeric +/- adjuster with configurable range and step
LubaRating Star rating control with read-only display mode
LubaTabs Segmented control with matched geometry animation
LubaIconButton Icon buttons with 44pt touch targets

Layout & Containers

Component What It Does
LubaCard Container with elevation levels. Compose with .lubaPressable()
LubaSheet Bottom sheets with size presets and drag indicator
LubaDivider Horizontal/vertical dividers with optional labels

Feedback & Status

Component What It Does
LubaToast Info, success, warning, error notifications with auto-dismiss
LubaAlert Inline notification banner with semantic styles
LubaProgressBar Linear progress indicator
LubaCircularProgress Circular progress indicator
LubaSpinner Arc, pulse, dots, and breathe loading styles
LubaBadge Status badges with semantic colors
LubaTooltip Contextual help popup with auto-dismiss

Data Display

Component What It Does
LubaAvatar Image or initials with size variants
LubaIcon Standardized icon sizing with semantic colors
LubaCircledIcon Icons with circular backgrounds
LubaSkeleton Shimmer loading placeholders (text, circle, card, row)
LubaChip Dismissible filter/tag pill with selection state
LubaLink Inline text link with default, subtle, and external styles
LubaMenu Context menu with icons and destructive item support

Data Visualization

Component What It Does
LubaBarChart Vertical/horizontal bar chart with token-based styling
LubaGroupedBarChart Multi-series grouped bar chart with auto palette
LubaLineChart Line chart with optional area fill and point markers
LubaMultiLineChart Multi-series line chart
LubaPieChart Pie and donut chart (iOS 17+, uses SectorMark)
LubaSparkline Minimal inline trend chart for dashboards
LubaChartSkeleton Animated loading placeholder (bar/line styles)
LubaChartLegend Custom legend row for chart labels

Design Tokens

Every value has a name. No magic numbers.

Colors

LubaColors.textPrimary       // Main text
LubaColors.textSecondary     // Supporting text
LubaColors.accent            // Brand color (sage green)
LubaColors.success           // Positive states
LubaColors.warning           // Caution states
LubaColors.error             // Error states
LubaColors.surface           // Card backgrounds
LubaColors.background        // Page backgrounds

All colors are adaptive — they switch automatically between light and dark mode.

Spacing (4pt base grid)

LubaSpacing.xxs   // 2pt
LubaSpacing.xs    // 4pt
LubaSpacing.sm    // 8pt
LubaSpacing.md    // 12pt
LubaSpacing.lg    // 16pt
LubaSpacing.xl    // 24pt
LubaSpacing.xxl   // 32pt
LubaSpacing.xxxl  // 48pt
LubaSpacing.huge  // 64pt

Motion

LubaMotion.pressScale        // 0.97 — the sweet spot
LubaMotion.pressAnimation    // Quick spring with bounce
LubaMotion.stateAnimation    // Smooth state transitions
LubaMotion.micro             // Ultra-quick feedback
LubaMotion.gentle            // Soft transitions
LubaMotion.disabledOpacity   // 0.45

Typography

LubaTypography.largeTitle    // SF Rounded by default
LubaTypography.title
LubaTypography.headline
LubaTypography.body
LubaTypography.caption
// ... 13 presets total

Button Styles

LubaButton("Save", style: .primary) { }
LubaButton("Cancel", style: .secondary) { }
LubaButton("Delete", style: .destructive) { }
LubaButton("Learn More", style: .ghost) { }
LubaButton("Subtle", style: .subtle) { }

// With icon and loading state
LubaButton("Upload", style: .primary, isLoading: isUploading, icon: Image(systemName: "arrow.up")) {
    startUpload()
}

// Create your own style
struct BrandStyle: LubaButtonStyling {
    func backgroundColor(isPressed: Bool, colorScheme: ColorScheme) -> Color {
        isPressed ? .purple.opacity(0.8) : .purple
    }
    func foregroundColor(isPressed: Bool, colorScheme: ColorScheme) -> Color { .white }
    func borderColor(isPressed: Bool, colorScheme: ColorScheme) -> Color? { nil }
    var borderWidth: CGFloat { 0 }
    var defaultsToFullWidth: Bool { true }
    var haptic: LubaHapticStyle { .medium }
}

LubaButton("Custom", styling: BrandStyle()) { }

Configuration

Control the system globally via SwiftUI's environment:

// Use a preset
ContentView()
    .lubaConfig(.accessible)    // High contrast, bold text, larger touch targets

ContentView()
    .lubaConfig(.minimal)       // No animations, no haptics

ContentView()
    .lubaConfig(.debug)         // Debug outlines and a11y logging

// Customize inline
ContentView()
    .lubaConfig { config in
        config.hapticsEnabled = false
        config.highContrastMode = true
        config.useRoundedFont = false
    }

Components automatically respect these settings via @Environment(\.lubaConfig).

Available Settings

Property Default Description
hapticsEnabled true Enable haptic feedback globally
hapticIntensity 1.0 Haptic feedback intensity (0.0 - 1.0)
animationsEnabled true Enable animations globally
respectReducedMotion true Respect system reduced motion setting
animationSpeed 1.0 Animation duration multiplier
minimumTouchTarget 44 Minimum touch target size in points
useBoldText false Bold text for readability
highContrastMode false Increased contrast for semantic colors
useRoundedFont true SF Rounded (true) or SF Pro (false)
customFontFamily nil Custom font family override
defaultButtonStyle .primary Default button style
defaultCardElevation .low Default card elevation
defaultCornerRadius 12 Default corner radius
showDebugOutlines false Show component outlines for debugging
logA11yWarnings false Log accessibility warnings

Glass Effects

LubaUI includes a glass/frosted primitive that works across iOS versions:

// Apply glass to any view
VStack {
    Text("Frosted Content")
}
.padding(LubaSpacing.lg)
.lubaGlass(.regular, tint: LubaColors.accent)

// Three intensity levels
.lubaGlass(.subtle)      // Light frosting — toolbars, FABs
.lubaGlass(.regular)     // Standard — cards, tab bars
.lubaGlass(.prominent)   // Heavy — panels, modals

// Built into components
LubaButton("Action", style: .glass) { }
LubaCard(style: .glass) { content }
LubaToast("Saved", style: .success, useGlass: true)

Uses SwiftUI materials on iOS 16-25, ready for native Liquid Glass on iOS 26+. Automatically falls back to a solid surface when Reduce Transparency or High Contrast Mode is active.


MCP Server — AI-Native Design System

LubaUI includes an MCP (Model Context Protocol) server that makes the entire design system queryable by AI assistants like Claude. Instead of reading source files, your AI can look up tokens, validate values, and get component APIs instantly.

One command to set up, works from any project:

claude mcp add lubaui -- npx lubaui-mcp@latest

Using @latest ensures you always get the newest version of the server.

What your AI can do:

  • Read the full design system reference in one call (lubaui://reference/full)
  • Look up multiple components at once (lookup_components)
  • Generate migration mappings from your existing design system (plan_migration)
  • Validate spacing and radius values against the token scales
  • Get contextual recommendations for what you're building (suggest_tokens)

See mcp-server/README.md for all 10 tools and 4 resources.


Requirements

  • iOS 16.0+ / macOS 13.0+ / watchOS 9.0+ / tvOS 16.0+ / visionOS 1.0+
  • Swift 5.9+
  • Xcode 15.0+

Contributing

LubaUI welcomes contributions. When adding new components:

  1. Create component-specific tokens (e.g., LubaFooTokens)
  2. Use LubaMotion for all animations — never hardcode values
  3. Read @Environment(\.lubaConfig) for haptics and animations
  4. Use LubaColors semantic colors (never raw hex values)
  5. Extract reusable behavior to primitives
  6. Maintain backwards compatibility

See llms.txt for detailed architecture documentation.


License

LubaUI is available under the MIT License. See LICENSE for details.


Made with intention by Erman Akar

About

A composable SwiftUI design system with tokens, theming, and interactive primitives

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors