@1dance/flex
v1.0.0
Published
A SwiftUI-inspired declarative UI framework for React Native
Readme
@1dance/flex
A SwiftUI-inspired declarative UI framework for React Native. Build beautiful, type-safe mobile apps with a familiar, chainable API.
Table of Contents
- Installation
- Quick Start
- Core Concepts
- Layout Components
- Views
- Styling
- State Management
- Examples
- API Reference
Installation
npm install @1dance/flex
# or
yarn add @1dance/flexPeer Dependencies
npm install react react-nativeQuick Start
import React from 'react';
import {
DView,
VStack,
Text,
Button,
Color,
Font,
modifier,
style,
useStateValue,
EnvironmentProvider,
HorizontalAlignment,
} from '@1dance/flex';
export default function App() {
const [count, countBinding] = useStateValue(0);
return (
<EnvironmentProvider>
<DView modifiers={modifier().style(
style()
.flex(1)
.padding(20)
.background(Color.background.toString())
)}>
<VStack spacing={20} alignment={HorizontalAlignment.Center}>
{Text('Hello, Flex!')
.font(Font.largeTitle)
.foregroundColor(Color.blue)
.bold()
.render()}
{Text(`Count: ${count}`)
.font(Font.title)
.render()}
<Button
title="Increment"
action={() => countBinding.set(count + 1)}
style="borderedProminent"
/>
</VStack>
</DView>
</EnvironmentProvider>
);
}Core Concepts
DView Component
DView is the foundational view component that replaces React Native's View. It uses modifiers instead of the style prop.
import { DView, modifier, style } from '@1dance/flex';
<DView modifiers={modifier().style(
style()
.flex(1)
.padding(20)
.background('#ffffff')
)}>
{/* children */}
</DView>Modifier System
The modifier system provides a chainable API combining style() for layout properties and swift() for interactions/accessibility.
import { modifier, style, swift } from '@1dance/flex';
<DView modifiers={modifier()
.style(style() // ViewStyle properties
.flex(1)
.padding(20)
.background('#fff')
.cornerRadius(12)
)
.swift(swift() // SwiftUI-like modifiers
.onTapGesture(() => console.log('Tapped!'))
.accessibilityLabel('My View')
)
}>Style Builder (style)
The style() builder handles all React Native ViewStyle properties:
import { style } from '@1dance/flex';
const myStyle = style()
// Flexbox
.flex(1)
.flexDirection('row')
.justifyContent('space-between')
.alignItems('center')
// Sizing
.width(200)
.height(100)
.size(50) // width & height
.aspectRatio(16/9)
// Spacing
.padding(20)
.paddingHorizontal(16)
.margin(10)
.gap(8)
// Position
.position('absolute')
.top(0).right(0).bottom(0).left(0)
.zIndex(10)
// Background & Border
.background('#007AFF')
.cornerRadius(12)
.border('#ccc', 1)
// Shadow
.shadow({ radius: 8, x: 0, y: 4, opacity: 0.2 })
// Visibility
.opacity(0.9)
.overflow('hidden');Preset Styles
import { Styles } from '@1dance/flex';
<DView modifiers={modifier().style(Styles.card)} />
<DView modifiers={modifier().style(Styles.centered)} />
<DView modifiers={modifier().style(Styles.row)} />
<DView modifiers={modifier().style(Styles.absoluteFill)} />Swift Modifiers (swift)
The swift() builder handles interactions, transforms, and accessibility:
import { swift, Font, Color } from '@1dance/flex';
const mySwiftModifiers = swift()
// Typography
.font(Font.title)
.bold()
.foregroundColor(Color.blue)
// Transforms
.rotation(45)
.scale(1.2)
.offset(10, 20)
// Clipping
.clipCircle()
.clipRoundedRect(12)
// Gestures
.onTapGesture(() => console.log('Tapped'))
.onLongPressGesture(() => console.log('Long pressed'))
.disabled(false)
// Accessibility
.accessibilityLabel('My Button')
.accessibilityRole('button');Layout Components
VStack
Vertical stack layout. Default alignment is Leading (left-aligned).
import { VStack, HorizontalAlignment } from '@1dance/flex';
// Left-aligned (default)
<VStack spacing={16}>
{Text('First').render()}
{Text('Second').render()}
</VStack>
// Centered
<VStack spacing={16} alignment={HorizontalAlignment.Center}>
{Text('Centered').render()}
</VStack>HStack
Horizontal stack layout.
import { HStack, Spacer } from '@1dance/flex';
<HStack spacing={12}>
{Text('Left').render()}
<Spacer />
{Text('Right').render()}
</HStack>ZStack
Overlapping stack layout.
import { ZStack, Alignments } from '@1dance/flex';
<ZStack alignment={Alignments.center}>
{Image('background.jpg').render()}
{Text('Overlay').render()}
</ZStack>Spacer
Flexible space that expands to fill available space.
<HStack>
{Text('Left').render()}
<Spacer />
{Text('Right').render()}
</HStack>Views
Text
Declarative text with chainable modifiers.
import { Text, Font, Color } from '@1dance/flex';
{Text('Hello World')
.font(Font.title)
.foregroundColor(Color.blue)
.bold()
.italic()
.underline()
.lineLimit(2)
.render()}Image
Declarative image with modifiers.
import { Image } from '@1dance/flex';
{Image('https://example.com/image.jpg')
.resizable()
.aspectRatio(16/9)
.frame({ width: 200, height: 112 })
.cornerRadius(8)
.render()}Button
Interactive button with multiple styles.
import { Button } from '@1dance/flex';
<Button title="Default" action={() => {}} style="default" />
<Button title="Bordered" action={() => {}} style="bordered" />
<Button title="Prominent" action={() => {}} style="borderedProminent" />
<Button title="Borderless" action={() => {}} style="borderless" />Toggle
Switch control.
import { Toggle, useStateValue } from '@1dance/flex';
const [isOn, binding] = useStateValue(false);
<Toggle
isOn={isOn}
onToggle={(value) => binding.set(value)}
label="Enable Feature"
/>Divider
Visual separator line.
import { Divider } from '@1dance/flex';
<Divider />Styling
Color System
Semantic colors that adapt to light/dark mode.
import { Color } from '@1dance/flex';
// Semantic colors
Color.label // Primary text
Color.secondaryLabel // Secondary text
Color.background // Primary background
Color.separator // Divider lines
// System colors
Color.blue
Color.green
Color.red
Color.orange
// Custom colors
Color.hex('#FF5733')
Color.rgb(255, 87, 51)
// With opacity
Color.blue.opacity(0.5)Font System
Type-safe font definitions.
import { Font } from '@1dance/flex';
Font.largeTitle // 34pt
Font.title // 28pt
Font.title2 // 22pt
Font.headline // 17pt, semibold
Font.body // 17pt
Font.caption // 12pt
// Custom font
Font.system(20, 'bold')State Management
useStateValue
React hook for state with binding support.
import { useStateValue } from '@1dance/flex';
function Counter() {
const [count, countBinding] = useStateValue(0);
return (
<VStack>
{Text(`Count: ${count}`).render()}
<Button
title="Increment"
action={() => countBinding.set(count + 1)}
/>
</VStack>
);
}Examples
Hello World
import React from 'react';
import {
DView,
Text,
VStack,
Color,
Font,
modifier,
style,
swift,
HorizontalAlignment,
} from '@1dance/flex';
export function HelloWorldExample() {
return (
<DView modifiers={modifier().style(
style()
.flex(1)
.padding(20)
)}>
<VStack spacing={16} alignment={HorizontalAlignment.Center}>
{Text('Hello, Flex!')
.font(Font.largeTitle)
.foregroundColor(Color.blue)
.bold()
.render()}
{Text('Build beautiful React Native apps')
.font(Font.title2)
.foregroundColor(Color.secondaryLabel)
.render()}
{Text('with a SwiftUI-inspired API')
.font(Font.body)
.italic()
.foregroundColor(Color.gray)
.render()}
<DView modifiers={modifier()
.style(style().padding(16).background(Color.blue.toString()).cornerRadius(10))
.swift(swift()
.onTapGesture(() => console.log('Button tapped!'))
.accessibilityLabel('Welcome button')
.accessibilityRole('button')
)
}>
{Text('Get Started')
.font(Font.headline)
.foregroundColor(Color.white)
.render()}
</DView>
</VStack>
</DView>
);
}Counter
import React from 'react';
import {
DView,
Text,
Button,
VStack,
HStack,
Color,
Font,
modifier,
style,
useStateValue,
HorizontalAlignment,
} from '@1dance/flex';
export function CounterExample() {
const [count, countBinding] = useStateValue(0);
return (
<DView modifiers={modifier().style(
style()
.flex(1)
.padding(20)
)}>
<VStack spacing={24} alignment={HorizontalAlignment.Center}>
{Text('Counter').font(Font.title).bold().render()}
{Text(`${count}`)
.font(Font.system(64))
.foregroundColor(count >= 0 ? Color.blue : Color.red)
.render()}
<HStack spacing={16}>
<Button title=" − " action={() => countBinding.set(count - 1)} style="bordered" />
<Button title="Reset" action={() => countBinding.set(0)} style="borderless" />
<Button title=" + " action={() => countBinding.set(count + 1)} style="bordered" />
</HStack>
<Button title="Add 10" action={() => countBinding.set(count + 10)} style="borderedProminent" />
</VStack>
</DView>
);
}Profile Card
import React from 'react';
import { ScrollView, Image as RNImage } from 'react-native';
import {
DView,
Text,
Button,
VStack,
HStack,
Color,
Font,
modifier,
style,
swift,
useStateValue,
HorizontalAlignment,
} from '@1dance/flex';
export function ProfileCardExample() {
const [isFollowing, followingBinding] = useStateValue(false);
return (
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 20 }}>
<DView modifiers={modifier()
.style(
style()
.background('#F2F2F7')
.cornerRadius(16)
.padding(20)
.alignItems('center')
.shadow({ radius: 8, y: 4, opacity: 0.1 })
)
.swift(swift().accessibilityLabel('Profile card for Sarah Johnson'))
}>
{/* Avatar */}
<DView modifiers={modifier()
.style(style().size(100).marginBottom(16))
.swift(swift().clipCircle())
}>
<RNImage
source={{ uri: 'https://i.pravatar.cc/150' }}
style={{ width: '100%', height: '100%' }}
/>
</DView>
<VStack spacing={4} alignment={HorizontalAlignment.Center}>
{Text('Sarah Johnson').font(Font.title2).bold().render()}
{Text('@sarahj')
.font(Font.subheadline)
.foregroundColor(Color.secondaryLabel)
.render()}
</VStack>
{/* Stats */}
<DView modifiers={modifier().style(style().marginVertical(16))}>
<HStack spacing={32}>
<VStack alignment={HorizontalAlignment.Center}>
{Text('1.2K').font(Font.headline).bold().render()}
{Text('Posts').font(Font.caption).foregroundColor(Color.secondaryLabel).render()}
</VStack>
<VStack alignment={HorizontalAlignment.Center}>
{Text('45.8K').font(Font.headline).bold().render()}
{Text('Followers').font(Font.caption).foregroundColor(Color.secondaryLabel).render()}
</VStack>
</HStack>
</DView>
<Button
title={isFollowing ? 'Following' : 'Follow'}
action={() => followingBinding.set(!isFollowing)}
style={isFollowing ? 'bordered' : 'borderedProminent'}
/>
</DView>
</ScrollView>
);
}Form with Validation
import React from 'react';
import { ScrollView, TextInput, Switch, Text as RNText } from 'react-native';
import {
DView,
Text,
Button,
HStack,
Color,
Font,
modifier,
style,
useStateValue,
} from '@1dance/flex';
export function FormExample() {
const [form, formBinding] = useStateValue({
name: '',
email: '',
agreedToTerms: false,
});
const [errors, errorsBinding] = useStateValue<Record<string, string>>({});
const validate = () => {
const newErrors: Record<string, string> = {};
if (!form.name.trim()) newErrors.name = 'Name is required';
if (!form.email.trim()) newErrors.email = 'Email is required';
if (!form.agreedToTerms) newErrors.terms = 'You must agree';
errorsBinding.set(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = () => {
if (validate()) {
console.log('Form submitted:', form);
}
};
return (
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 20 }}>
{Text('Sign Up').font(Font.title).bold().render()}
<DView modifiers={modifier().style(style().marginTop(24).gap(20))}>
{/* Name Field */}
<DView modifiers={modifier().style(style().gap(8))}>
{Text('Name').font(Font.headline).render()}
<TextInput
value={form.name}
onChangeText={(name) => formBinding.set({ ...form, name })}
placeholder="Enter your name"
style={{
padding: 16,
backgroundColor: '#F5F5F5',
borderRadius: 10,
fontSize: 16,
}}
/>
{errors.name && (
<RNText style={{ color: '#FF3B30', fontSize: 12 }}>{errors.name}</RNText>
)}
</DView>
{/* Email Field */}
<DView modifiers={modifier().style(style().gap(8))}>
{Text('Email').font(Font.headline).render()}
<TextInput
value={form.email}
onChangeText={(email) => formBinding.set({ ...form, email })}
placeholder="Enter your email"
keyboardType="email-address"
style={{
padding: 16,
backgroundColor: '#F5F5F5',
borderRadius: 10,
fontSize: 16,
}}
/>
{errors.email && (
<RNText style={{ color: '#FF3B30', fontSize: 12 }}>{errors.email}</RNText>
)}
</DView>
{/* Terms Toggle */}
<HStack spacing={12}>
{Text('I agree to the Terms').font(Font.body).render()}
<Switch
value={form.agreedToTerms}
onValueChange={(agreedToTerms) => formBinding.set({ ...form, agreedToTerms })}
/>
</HStack>
<Button
title="Create Account"
action={handleSubmit}
style="borderedProminent"
/>
</DView>
</ScrollView>
);
}API Reference
Components
| Component | Description |
|-----------|-------------|
| DView | Base view with modifier support |
| VStack | Vertical stack (default: left-aligned) |
| HStack | Horizontal stack |
| ZStack | Overlapping stack |
| Spacer | Flexible space |
| Divider | Visual separator |
| Button | Interactive button |
| Toggle | Switch control |
Declarative Views
| View | Description |
|------|-------------|
| Text(string) | Text with chainable modifiers |
| Image(source) | Image with chainable modifiers |
Builders
| Builder | Description |
|---------|-------------|
| modifier() | Creates ModifierBuilder |
| style() | Creates DStyleBuilder for ViewStyle |
| swift() | Creates DSwiftBuilder for interactions |
Hooks
| Hook | Description |
|------|-------------|
| useStateValue(initial) | State with binding |
| useEnvironment() | Environment access |
| useColorSchemeValue() | Current color scheme |
License
MIT © 1Dance
