"I want it to feel like walking alone through a quiet museum."
That was the first thing I typed when I tried to get Rork to build an app for showing my own work. As a technical instruction it is hopeless. But what I had in my head was not a screen spec — it was that texture.
If you came to app development because you wanted a container for your work, that order is hard to reverse. The feeling arrives before the feature list. The trouble is that this vocabulary doesn't reach the implementation.
What changed for me with Rork is that translation step. Below is the actual working record: what I threw at it, what came back, and how I pinned the result down by hand.
The Particular Way Creators Get Stuck
Programmers tend to assemble a screen outward from capability. Creators start from the experience. That difference in ordering produces a specific kind of early confusion.
The image of the screen is vivid, but the words that would turn it into instructions for code or AI simply aren't there. Try to learn the technical terms first, and the experiential image starts drifting away instead. That back-and-forth was the exhausting part when I first picked up Rork.
Tools like Rork earn their place precisely in that gap — they take the first leg of the translation. But hand it off and walk away, and the translation comes back slightly different every time. How you handle that is what this piece is really about.
Prompting From Emotion vs. Prompting From Function
I once asked for the same gallery screen two different ways.
❌ Written from function
Make a gallery app that displays photos in a grid and
shows them fullscreen when tapped.
✅ Written from emotion
I want to recreate the feeling of walking alone through a quiet museum.
Works (photos) arranged in a single vertical column with generous
breathing room, fading in slowly when tapped to open.
No music — a dark, hushed screen.
Touch feedback should stay understated.
The first returned a three-column grid on a white background with instant transitions — a screen you have seen a hundred times. The second returned a single column, a dark ground, and a detail view that faded in. Much closer to what was in my head.
I had braced for the opposite: surely the vaguer prompt would degrade. It didn't. Words like "quiet," "breathing room," and "slowly" land fairly honestly as concrete choices — amounts of padding, animation durations, desaturated color. For finer prompt technique, pulling the exact UI you want out of Rork goes deeper.
Landing honestly and landing reproducibly, though, turned out to be two different things.
Giving the Mood a Number
Reading the implementation behind that "slow fade," the duration came back around 400ms. Fine by feel. Then I asked for one more screen, and that one came back at 250ms. Same word, different number every generation.
So I started reading the output, picking out the values I liked, pinning them in one place, and pointing every later prompt at that file.
// theme/tokens.ts — the values I liked, collected in one place
export const tokens = {
space: { edge: 20, betweenWorks: 24, gallery: 32 },
motion: { fadeIn: 420, press: 120 },
color: {
bg: '#12100E',
surface: '#1C1A17',
text: '#E8E3DA',
muted: '#9A9184',
accent: '#C0703C',
},
} as const;Screens then read only from that file.
// components/WorkImage.tsx
import { useEffect, useRef } from 'react';
import { Animated, Easing, Pressable, StyleSheet } from 'react-native';
import { tokens } from '../theme/tokens';
export function WorkImage({ uri, onPress }: { uri: string; onPress: () => void }) {
const opacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: tokens.motion.fadeIn, // 420ms — "slowly," turned into a number
easing: Easing.out(Easing.quad),
useNativeDriver: true,
}).start();
}, [opacity]);
return (
<Pressable onPress={onPress} style={styles.frame}>
<Animated.Image
source={{ uri }}
style={[styles.image, { opacity }]}
resizeMode="contain"
/>
</Pressable>
);
}
const styles = StyleSheet.create({
frame: {
padding: tokens.space.edge,
marginBottom: tokens.space.betweenWorks,
backgroundColor: tokens.color.bg,
},
image: { width: '100%', aspectRatio: 3 / 4, borderRadius: 2 },
});useNativeDriver: true matters here: it moves the opacity animation off the JS thread. Leave it out and stack a few dozen works in a scroll view, and the fades start catching during scroll.
Once the file exists, the next prompt stops being "fade it in slowly" and becomes "fade in using motion.fadeIn from theme/tokens.ts." That's a reference rather than an interpretation, and the drift stops.
What I had really done was give my own instincts a unit.
| What I meant | What came back | What I pinned |
|---|---|---|
| Fade in slowly | Animated.timing + Easing.out | motion.fadeIn = 420 (ms) |
| Don't crowd the works together | Rhythm via marginBottom | space.betweenWorks = 24 (px) |
| Quiet breathing room | Padding and a dark ground | space.edge = 20 / color.bg = #12100E |
| Understated touch feedback | A slight opacity shift | motion.press = 120 (ms) |
The general pattern — collecting values in one place so regeneration can't scatter them — is laid out in stopping color and spacing drift with design tokens.
Moving Your Artwork's Palette Into the App
Color you can start even earlier, because the work you want to display already carries a palette.
I pull a handful of colors from the piece and hand them over with roles attached. Send bare hex codes and say "use these," and the accent ends up as a background somewhere, and the intent unravels.
Build a design system from this palette.
#12100E — background (darker than the work; must not flatten it)
#E8E3DA — body text (readable in long passages)
#C0703C — accent (exactly one place per screen; do not spread it)
Define these in tokens.ts. Never write hex directly in a screen.
What's inside the parentheses isn't a description of the color — it's the rule for using it. Write that down and you're far less likely to find #C0703C later scattered across buttons, tabs, and badges at once.
One thing you cannot leave to instinct: contrast between text and background. Choose colors that flatter the artwork and the text sinks. My secondary text started at #8A8378 and was genuinely hard to read; I lifted it to #9A9184. WCAG AA asks for a contrast ratio of at least 4.5:1 for body-sized text. I understand the urge to protect the mood, but text you can't read isn't mood — it's a defect.
If you'd rather settle the design in Figma first and hand that over, taking a Figma design into a Rork app covers that route.
Where It Stops — Fonts, Regeneration, Review
Writing only about what worked wouldn't be much use, so here's where I actually stalled.
Font loading and rights. You'll want a display face that matches the work. Check the commercial license for embedding before you do. Even when licensing is clean, the text can render in the fallback face for a moment and visibly swap. I held the splash screen until expo-font finished loading.
// app/_layout.tsx
import { useEffect } from 'react';
import { Stack } from 'expo-router';
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const [loaded] = useFonts({
'Display-Regular': require('../assets/fonts/Display-Regular.otf'),
});
useEffect(() => {
if (loaded) SplashScreen.hideAsync();
}, [loaded]);
if (!loaded) return null;
return <Stack screenOptions={{ headerShown: false }} />;
}The causes and fixes for the flicker itself live in stopping custom font flicker in a Rork app.
Hand edits vanishing on regeneration. This one stung the most. The contrast-adjusted colors I'd tuned by hand were written straight back over during a later feature regeneration. Decide up front which areas you own and which Rork owns, and the accidents mostly stop. Letting Rork's regeneration and your manual edits coexist is the practical treatment of that boundary.
The work sitting in front of review. You can build the app, but certificates, provisioning profiles, and guideline checks remain. That part is outside generation. Reading the route to publishing a Rork app on the App Store before you finish building saves a scramble at the end.
Moving on without reading the generated code. It's fine not to understand it at first. But when you want to change something and don't know why it's built that way, you have no way to phrase the instruction. You don't need to read all of it — following just the parts you cared about makes every later prompt shorter.
Placing the App as a Piece of Work
What Rork changed for me was the ordering.
There used to be a step: master the technology first, then build the thing you imagined. Now you can start from the feeling and let Rork carry the first leg of the translation. The second leg — giving what comes back a unit, and pinning it so it stops drifting — is still in your hands. And I suspect that second leg is what decides whether the thing feels like your work at all.
Start with one screen showing five of your pieces, asked for in a single sentence written from emotion. Read the spacing and timing that come back, and move the numbers you like into tokens.ts. That's one full round trip of the translation. Pairing it with building your first Rork app in 30 minutes makes it an easy place to start.
I'm still improving the accuracy of that translation myself. If any of this is useful to someone stuck at the same spot, I'm glad. Thank you for reading.