Native & Mobile Integration

pudge-ui specs translate to any rendering platform. The CSS properties map directly to native APIs: gradients become platform gradient constructors, shadows become platform shadow APIs, border-radius becomes corner radius, and the three-plane depth model works everywhere.

SwiftUI (iOS/macOS)

SwiftUI modifiers map cleanly to CSS properties. The depth model uses .shadow() for bottom edges and .overlay() with stroked shapes for top highlights.

import SwiftUI

struct PushButton: View {
    let label: String
    var size: ButtonSize = .md
    var material: ButtonMaterial = .panel
    var isActive: Bool = false
    var action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(label)
                .font(.system(size: size.fontSize, weight: .semibold))
                .tracking(1.5)
                .textCase(.uppercase)
                .foregroundColor(isActive ? Color("amber") : Color("textPrimary"))
        }
        .padding(.horizontal, size.hPadding)
        .padding(.vertical, size.vPadding)
        .background(
            LinearGradient(
                colors: [Color("bgPanel"), Color("bgSurface")],
                startPoint: .top, endPoint: .bottom
            )
        )
        .cornerRadius(8)
        // Three-plane depth model
        .shadow(color: .black.opacity(0.4), radius: 0, y: 2)           // bottom edge
        .overlay(
            RoundedRectangle(cornerRadius: 8)
                .stroke(Color.white.opacity(0.14), lineWidth: 1)        // top highlight
                .offset(y: -0.5)
        )
    }
}

// Size variants mirror the CSS spec
enum ButtonSize {
    case xs, sm, md, lg, xl
    var fontSize: CGFloat {
        switch self {
        case .xs: return 8; case .sm: return 9
        case .md: return 10; case .lg: return 11; case .xl: return 13
        }
    }
    var hPadding: CGFloat {
        switch self {
        case .xs: return 8; case .sm: return 10
        case .md: return 14; case .lg: return 18; case .xl: return 22
        }
    }
    var vPadding: CGFloat {
        switch self {
        case .xs: return 4; case .sm: return 5
        case .md: return 6; case .lg: return 8; case .xl: return 10
        }
    }
}

Jetpack Compose (Android)

Compose Modifier chains mirror CSS property stacks. Brush.verticalGradient replaces linear-gradient, and shadow() handles depth.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

// pudge-ui tokens
val bgPanel = Color(0xFF22201E)
val bgSurface = Color(0xFF2A2826)
val textPrimary = Color(0xFFD8D4CC)
val amber = Color(0xFFF5A623)

@Composable
fun PushButton(
    label: String,
    isActive: Boolean = false,
    onClick: () -> Unit
) {
    Box(
        modifier = Modifier
            .shadow(2.dp, RoundedCornerShape(8.dp))
            .background(
                brush = Brush.verticalGradient(
                    colors = listOf(bgPanel, bgSurface)
                ),
                shape = RoundedCornerShape(8.dp)
            )
            .padding(horizontal = 14.dp, vertical = 6.dp)
    ) {
        Text(
            text = label.uppercase(),
            color = if (isActive) amber else textPrimary,
            fontSize = 10.sp,
            fontWeight = FontWeight.SemiBold,
            letterSpacing = 1.5.sp
        )
    }
}

Flutter

Flutter's BoxDecoration maps directly to the CSS box model. LinearGradient replaces linear-gradient, BoxShadow replaces box-shadow.

import 'package:flutter/material.dart';

// pudge-ui tokens
const bgPanel = Color(0xFF22201E);
const bgSurface = Color(0xFF2A2826);
const textPrimary = Color(0xFFD8D4CC);
const amber = Color(0xFFF5A623);
const borderDeep = Color(0xFF0A0908);

class PushButton extends StatelessWidget {
  final String label;
  final bool isActive;
  final VoidCallback? onPressed;

  const PushButton({
    super.key,
    required this.label,
    this.isActive = false,
    this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onPressed,
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
        decoration: BoxDecoration(
          // Three-plane depth: gradient + shadow + highlight
          gradient: const LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [bgPanel, bgSurface],
          ),
          borderRadius: BorderRadius.circular(8),
          boxShadow: const [
            BoxShadow(color: borderDeep, offset: Offset(0, 2)),
          ],
          border: Border(
            top: BorderSide(
              color: Colors.white.withValues(alpha: 0.14),
              width: 1,
            ),
          ),
        ),
        child: Text(
          label.toUpperCase(),
          style: TextStyle(
            color: isActive ? amber : textPrimary,
            fontSize: 10,
            fontWeight: FontWeight.w600,
            letterSpacing: 1.5,
          ),
        ),
      ),
    );
  }
}

React Native

React Native uses StyleSheet.create with platform-specific shadow handling. iOS gets shadowColor/Offset/Opacity, Android gets elevation.

import { Pressable, Text, StyleSheet, Platform } from 'react-native';

// pudge-ui tokens
const tokens = {
  bgPanel: '#22201e',
  bgSurface: '#2a2826',
  textPrimary: '#d8d4cc',
  amber: '#f5a623',
  borderDeep: '#0a0908',
};

export function PushButton({ label, active, onPress }) {
  return (
    <Pressable
      onPress={onPress}
      style={({ pressed }) => [
        styles.button,
        active && styles.active,
        pressed && styles.pressed,
      ]}
    >
      <Text style={[
        styles.label,
        active && styles.activeLabel,
      ]}>
        {label.toUpperCase()}
      </Text>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  button: {
    paddingHorizontal: 14,
    paddingVertical: 6,
    backgroundColor: tokens.bgPanel,
    borderRadius: 8,
    // Three-plane depth (platform-specific shadows)
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.4,
        shadowRadius: 0,
      },
      android: { elevation: 3 },
    }),
    borderTopWidth: 1,
    borderTopColor: 'rgba(255,255,255,0.14)',
  },
  pressed: {
    transform: [{ translateY: 1 }],
    ...Platform.select({
      ios: { shadowOffset: { width: 0, height: 1 } },
      android: { elevation: 1 },
    }),
  },
  active: {
    borderColor: tokens.amber,
    borderWidth: 1,
  },
  label: {
    color: tokens.textPrimary,
    fontSize: 10,
    fontWeight: '600',
    letterSpacing: 1.5,
  },
  activeLabel: {
    color: tokens.amber,
  },
});

Translation Patterns

Every pudge-ui CSS property has a native equivalent:

CSS PropertySwiftUIComposeFlutter
linear-gradientLinearGradientBrush.verticalGradientLinearGradient
box-shadow.shadow()Modifier.shadow()BoxShadow
border-radius.cornerRadius()RoundedCornerShapeBorderRadius.circular()
inset box-shadow.overlay() + strokedrawBehind Border(top:)
letter-spacing.tracking()letterSpacingletterSpacing
font-weight: 600.semiboldFontWeight.SemiBoldFontWeight.w600
text-transform: uppercase.textCase(.uppercase).uppercase().toUpperCase()
transition.animation()animateAs*()AnimatedContainer

pudge-ui specs describe visual physics (materials, depth, light), not web-specific APIs. Any rendering engine that can draw gradients, shadows, and rounded rectangles can implement the full spec.

Gradients, shadows, and corner radius work on every platform. The physics are universal.

Pudge