I wanted to see how well Kiro understands Vue 3 idioms, so I built a fun app in a single session: an Auntie Tigress Tracker inspired by the classic Taiwanese folk tale θε§ε© β a tiger spirit from the mountains who sneaks into homes at night to snatch children who won't fall asleep.
The app shows a watercolor map tracking a creature creeping toward your home in real-time. Too scary? Swap the tiger for a puppy πΆ or a ghost π». It uses your browser's geolocation (or defaults to Taipei).
π Live demo | Source code
What I Prompted
I gave Kiro one high-level prompt describing the app β Vue 3 Composition API, <script setup>, Leaflet map, composables for geolocation and creature tracking, an icon picker with defineProps/defineEmits β then refined individual pieces. Here's what stood out.
Composables β ref vs reactive
Kiro correctly chose ref() for the geolocation position (we replace the whole object on each update) and reactive() for the creature state (we mutate individual properties like lat, lng, icon). This is a subtle distinction that trips up even experienced Vue devs.
// useGeolocation.js β ref for replaceable state
const position = ref({ lat: 25.033, lng: 121.565 })
watchId = navigator.geolocation.watchPosition(
(pos) => {
position.value = { lat: pos.coords.latitude, lng: pos.coords.longitude }
},
(err) => { error.value = err.message },
{ enableHighAccuracy: true, timeout: 10000 }
)
// useCreatureTracker.js β reactive for mutable state
const creature = reactive({ lat: 0, lng: 0, icon: 'π―', name: 'Auntie Tigress' })
watch(homePosition, (home) => {
radius = 0.008 + Math.random() * 0.004
updatePosition(home)
}, { immediate: true })
It also used watchPosition over getCurrentPosition for mobile reliability (especially iOS), and cleaned up properly with clearWatch in onUnmounted. The creature tracker uses watch with { immediate: true } to initialize position as soon as geolocation resolves.
Props + Emits β The Classic Vue Pattern
The icon picker passes data down via defineProps, communicates up via defineEmits:
<script setup>
const props = defineProps({
options: { type: Array, required: true },
selected: { type: String, required: true },
})
const emit = defineEmits(['select'])
</script>
<template>
<button
v-for="opt in options"
:key="opt.id"
:class="{ active: selected === opt.icon }"
@click="emit('select', opt.id)"
>
{{ opt.icon }} {{ opt.name }}
</button>
</template>
Type-validated props, a clean emit contract, :class binding for reactive styling, v-for with :key β all idiomatic.
Computed Chains β Reactive All the Way Down
The map component chains computed properties β distance β label β danger level β all automatically reactive:
const distanceMeters = computed(() => {
// haversine formula
return Math.round(2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)))
})
const distanceLabel = computed(() =>
distanceMeters.value > 1000
? (distanceMeters.value / 1000).toFixed(1) + ' km'
: distanceMeters.value + ' m'
)
const dangerLevel = computed(() => {
const d = distanceMeters.value
if (d < 300) return { text: 'GO TO SLEEP NOW! π±', color: '#ff4444' }
if (d < 600) return { text: 'Getting closeβ¦ π°', color: '#ff8844' }
if (d < 1000) return { text: 'Nearbyβ¦ stay alert π', color: '#ffaa44' }
return { text: 'Safeβ¦ for now π', color: '#88cc66' }
})
When the creature moves (every 1.5s), every computed updates automatically. No manual subscriptions, no dependency arrays.
Bridging Vue Reactivity with Leaflet
Leaflet is imperative β you call setLatLng(), setIcon() directly. Kiro used watch with { deep: true } to bridge Vue's reactivity into Leaflet's world, and fitBounds to ensure both markers are visible on first load:
watch(() => props.creature, (c) => {
creatureMarker?.setLatLng([c.lat, c.lng])
creatureMarker?.setIcon(emojiIcon(c.icon, 38))
creatureMarker?.setTooltipContent(c.name)
}, { deep: true })
Verdict
Kiro nailed the Vue 3 patterns: ref vs reactive, composables with lifecycle hooks, defineProps/defineEmits, computed chains, and watch with { deep: true } for imperative library integration. It felt like pair-programming with someone who actually writes Vue.
Try it yourself β one prompt, one session, one fun app. π
Built with Kiro and Vue 3. No children were eaten in the making of this app. π―

Top comments (0)