Generate 3D perspective UI components with rotation
Design-style skill for 3D UI: true Z-axis depth, interactive rotation, and perspective, with Web/SwiftUI/Flutter code.
Why it matters
Developers hire this skill to create interactive three-dimensional user interface components that respond to user input with realistic depth, perspective, and rotation effects across web and mobile platforms.
Outcomes
What it gets done
Generate CSS perspective transforms with rotateX/rotateY for web cards
Build SwiftUI components with rotation3DEffect and drag gestures
Create Flutter Matrix4 perspective transforms with pan gesture handlers
Implement React Native and Jetpack Compose 3D rotation animations
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-3d-ui | bash Overview
3D UI
A design-style skill for 3D, rotatable-space interfaces - true Z-axis depth, interactive tilt via mouse or gyroscope - with matching Web CSS, SwiftUI, and Flutter implementations. Use it for product cards, showcases, or panels that should feel physically manipulable in 3D space; it's a design sub-style selected via the parent design-it skill, not invoked directly.
What it does
3D UI is a design-style child skill ('breaking the plane') for interfaces that exist in a three-dimensional, rotatable space, part of the design-it skill family and not meant to be triggered directly. Three core principles: True Depth (elements physically move closer to or further from the camera via Z-axis translation, not just shadows), Perspective (CSS perspective or WebGL to create realistic vanishing points), and Interactive Rotation (elements respond to mouse movement or device gyroscope by tilting or rotating in 3D space).
When to use - and when NOT to
Use it when the user's request matches this 3D, depth-driven aesthetic - card interfaces, product showcases, or interactive panels that should feel physically manipulable in space rather than flat. As a design sub-style, it's selected by the parent design-it skill's routing, not invoked on its own.
Inputs and outputs
Visual DNA: bold, striking palettes like Midnight Luxury or Industrial Chic, since 3D elements need high contrast to reveal their geometry; bold, blocky, or extruded typography; and rendered 3D assets (.glb, .gltf, or high-res PNGs of 3D objects) instead of flat icons. Web implementation relies on perspective, transform-style: preserve-3d, and rotateX/rotateY - a card starts at a slight rotateX(15deg) rotateY(-15deg) tilt, straightens and pushes toward the viewer on hover (translateZ(50px)), with inner content pushed forward independently via its own translateZ. SwiftUI implements the same effect with .rotation3DEffect() on two axes linked to a drag gesture, using the perspective parameter (default 1/6, higher values distort more) to control camera distance, and can link rotation to CoreMotion gyroscope input for a fully interactive 3D card. Flutter achieves the equivalent via a GestureDetector tracking pan offset with a spring-back animation on release - the perspective trick specifically is Matrix4.identity()..setEntry(3, 2, 0.001) applied inside a Transform widget, with TweenAnimationBuilder smoothing the return-to-center physics. React Native uses PanResponder and an Animated.ValueXY to track drag position, springing back to {x: 0, y: 0} on release with Animated.spring.
Integrations
All three platform implementations share the same interaction model - drag or gyroscope input maps directly to rotation angle on two axes, with a spring-based snap-back to neutral when the gesture ends - so the 3D feel stays consistent whether the surface is CSS, SwiftUI, or Flutter.
Who it's for
Designers and frontend developers building an interface that should feel spatial and physically manipulable - product cards, showcases, interactive panels - rather than a flat 2D layout with only shadow-based depth cues.
Source README
3D UI
"Breaking the plane. Interfaces that exist in a three-dimensional, rotatable space."
When to Use
Use this sub-style when the user's request matches the aesthetic described above. This is a child reference of the design-it skill and is not meant to be triggered directly.
Core Principles
- True Depth (Z-Axis Translation): Elements don't just have shadows; they physically move closer to or further from the camera.
- Perspective: Use CSS perspective or WebGL to create realistic vanishing points.
- Interactive Rotation: Elements should respond to mouse movement or device gyroscope by tilting or rotating in 3D space.
Visual DNA
- Colors: Bold, striking palettes like Midnight Luxury or Industrial Chic. 3D elements need high contrast to show their geometry.
- Typography: Bold, blocky, or extruded text.
- Graphics: Instead of flat icons, use rendered 3D assets (.glb, .gltf, or high-res PNGs of 3D objects).
Web Implementation
- Rely heavily on
perspective,transform-style: preserve-3d, androtateX/rotateY. - CSS Example:
.perspective-container {
perspective: 1000px;
display: flex;
justify-content: center;
align-items: center;
}
.card-3d {
width: 300px;
height: 400px;
transform-style: preserve-3d;
transition: transform 0.5s ease;
/* Initial slight rotation */
transform: rotateX(15deg) rotateY(-15deg);
}
.card-3d:hover {
/* Straighten out on hover */
transform: rotateX(0) rotateY(0) translateZ(50px);
}
/* Inner elements popping out */
.card-content {
transform: translateZ(30px); /* Pushes content 30px closer to viewer */
}
App Implementation
SwiftUI
struct Card3D: View {
@State private var dragOffset = CGSize.zero
var body: some View {
VStack {
Text("3D Card")
.font(.largeTitle.bold())
.foregroundColor(.white)
}
.frame(width: 300, height: 400)
.background(
LinearGradient(colors: [.blue, .purple], startPoint: .topLeading, endPoint: .bottomTrailing)
)
.cornerRadius(24)
.shadow(radius: 20)
// Magic 3D effect based on drag gesture
.rotation3DEffect(
.degrees(Double(dragOffset.width / 10)),
axis: (x: 0, y: 1, z: 0),
perspective: 0.5
)
.rotation3DEffect(
.degrees(Double(-dragOffset.height / 10)),
axis: (x: 1, y: 0, z: 0),
perspective: 0.5
)
.gesture(
DragGesture()
.onChanged { value in
withAnimation(.interactiveSpring()) {
dragOffset = value.translation
}
}
.onEnded { _ in
withAnimation(.spring()) {
dragOffset = .zero
}
}
)
}
}
- SwiftUI makes this incredibly easy with
.rotation3DEffect(). - Use the
perspectiveparameter (default 1/6, higher = more distorted) to control the camera distance. - Link the rotation axes (
x,y) to drag gestures or CoreMotion (gyroscope) for interactive 3D UI.
Flutter
class Card3D extends StatefulWidget {
@override
State<Card3D> createState() => _Card3DState();
}
class _Card3DState extends State<Card3D> {
Offset _offset = Offset.zero;
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (details) {
setState(() => _offset += details.delta);
},
onPanEnd: (_) {
setState(() => _offset = Offset.zero); // Snap back
},
child: TweenAnimationBuilder(
tween: Tween<Offset>(begin: Offset.zero, end: _offset),
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
builder: (context, Offset offset, child) {
// Perspective Matrix
final transform = Matrix4.identity()
..setEntry(3, 2, 0.001) // perspective
..rotateX(-offset.dy * 0.01)
..rotateY(offset.dx * 0.01);
return Transform(
transform: transform,
alignment: FractionalOffset.center,
child: Container(
width: 300,
height: 400,
decoration: BoxDecoration(
gradient: const LinearGradient(colors: [Colors.blue, Colors.purple]),
borderRadius: BorderRadius.circular(24),
boxShadow: const [BoxShadow(color: Colors.black45, blurRadius: 20)],
),
alignment: Alignment.center,
child: const Text('3D Card',
style: TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold)),
),
);
},
),
);
}
}
- The secret to perspective in Flutter is
Matrix4.identity()..setEntry(3, 2, 0.001). - Wrap the target container in a
Transformwidget and apply rotations on the X and Y axes. - Use
TweenAnimationBuilderto smooth out the return-to-center physics.
React Native
const Card3D = () => {
const pan = useRef(new Animated.ValueXY()).current;
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }], { useNativeDriver: false }),
onPanResponderRelease: () => {
Animated.spring(pan, { toValue: { x: 0, y: 0 }, useNativeDriver: false }).start();
},
})
).current;
// Map drag distance to degrees
const rotateX = pan.y.interpolate({ inputRange: [-200, 200], outputRange: ['20deg', '-20deg'] });
const rotateY = pan.x.interpolate({ inputRange: [-200, 200], outputRange: ['-20deg', '20deg'] });
return (
<Animated.View
{...panResponder.panHandlers}
style={{
width: 300, height: 400,
backgroundColor: '#6b21a8',
borderRadius: 24,
justifyContent: 'center', alignItems: 'center',
// Pseudo-3D transforms
transform: [
{ perspective: 1000 },
{ rotateX },
{ rotateY }
]
}}
>
<Text style={{ color: '#fff', fontSize: 32, fontWeight: '700' }}>3D Card</Text>
</Animated.View>
);
};
- True 3D models require
react-three-fiber. - For UI perspective transforms, use the
transformarray:[{ perspective: 1000 }, { rotateX: '...' }, { rotateY: '...' }]. perspectiveMUST be the first item in the transform array for the effect to render correctly.
Jetpack Compose
@Composable
fun Card3D() {
var offset by remember { mutableStateOf(Offset.Zero) }
val animatedOffset by animateOffsetAsState(
targetValue = offset,
animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy)
)
Box(
modifier = Modifier
.size(300.dp, 400.dp)
.pointerInput(Unit) {
detectDragGestures(
onDrag = { change, dragAmount ->
change.consume()
offset += dragAmount
},
onDragEnd = { offset = Offset.Zero },
onDragCancel = { offset = Offset.Zero }
)
}
.graphicsLayer {
// Apply 3D rotation based on drag offset
rotationX = -animatedOffset.y * 0.1f
rotationY = animatedOffset.x * 0.1f
cameraDistance = 8f * density // Sets the perspective
}
.shadow(20.dp, RoundedCornerShape(24.dp))
.clip(RoundedCornerShape(24.dp))
.background(Brush.linearGradient(listOf(Color.Blue, Color.Magenta)))
) {
Text("3D Card",
color = Color.White, fontSize = 32.sp, fontWeight = FontWeight.Bold,
modifier = Modifier.align(Alignment.Center))
}
}
- Apply 3D transformations inside
Modifier.graphicsLayer { }. - Set
rotationXandrotationYfor the tilt. - Critical: Set
cameraDistanceto establish the Z-axis perspective vanishing point. Usually8f * densityis a good starting point.
Do's and Don'ts
- DO: Tie 3D rotation to user input (mouse move on web, device tilt on mobile) for maximum impact.
- DON'T: Make text itself 3D unless it's a massive headline. 3D text is generally unreadable at small sizes.
Limitations
- This is a styling reference and does not replace environment-specific validation, accessibility testing, or expert review.
- Ensure appropriate contrast ratios and responsive behaviors are verified separately.
FAQ
Common questions
Discussion
Questions & comments ยท 0
Sign In Sign in to leave a comment.