feat(machine): responsive mobile-friendly layout with Tailwind v4

- IdleView: stack cards vertically on mobile, side-by-side on sm+
- Scale logo, cards, text, and badges down for small screens
- QR overlays use smaller codes on mobile (280px vs 400px)
- Status badges wrap and use smaller text on mobile
- Hide subtitle text on mobile cards to save space

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-02-19 16:47:26 -05:00
commit a9da1067b3
2 changed files with 47 additions and 29 deletions

View file

@ -37,10 +37,12 @@ function toggleLiveServices() {
<router-view /> <router-view />
<!-- Connection status + balance (top right) --> <!-- Connection status + balance (top right) -->
<div class="fixed right-4 top-4 z-50 flex items-center gap-2"> <div
class="fixed right-2 top-2 z-50 flex flex-wrap items-center justify-end gap-1 sm:right-4 sm:top-4 sm:gap-2"
>
<Badge <Badge
v-if="atmStore.balanceSats !== null" v-if="atmStore.balanceSats !== null"
class="bg-bitcoin/20 font-mono text-bitcoin border border-bitcoin/30" class="bg-bitcoin/20 font-mono text-bitcoin border border-bitcoin/30 text-xs sm:text-sm"
> >
bal: {{ formatSats(atmStore.balanceSats) }} sats bal: {{ formatSats(atmStore.balanceSats) }} sats
</Badge> </Badge>

View file

@ -59,52 +59,66 @@ function handleCashOut() {
</script> </script>
<template> <template>
<div class="flex flex-1 flex-col items-center justify-center gap-8 p-8"> <div class="flex flex-1 flex-col items-center justify-center gap-4 p-4 sm:gap-6 md:gap-8 md:p-8">
<!-- Logo --> <!-- Logo -->
<div class="flex flex-col items-center gap-2"> <div class="flex flex-col items-center gap-1 sm:gap-2">
<img <img
src="/logo.png" src="/logo.png"
alt="Lamassu" alt="Lamassu"
class="h-40 w-40 animate-float drop-shadow-[0_0_24px_var(--primary)]" class="h-20 w-20 animate-float drop-shadow-[0_0_24px_var(--primary)] sm:h-28 sm:w-28 md:h-40 md:w-40"
/> />
<h1 class="text-4xl font-bold text-primary">Bitcoinmat</h1> <h1 class="text-2xl font-bold text-primary sm:text-3xl md:text-4xl">Bitcoinmat</h1>
<!-- Footer badges --> <!-- Footer badges -->
<div class="mt-4 flex items-center gap-4"> <div class="mt-2 flex flex-wrap items-center justify-center gap-2 sm:mt-4 sm:gap-4">
<Badge variant="outline" class="border-success text-success">No KYC</Badge> <Badge variant="outline" class="border-success text-success text-xs sm:text-sm"
<Badge variant="outline" class="border-primary text-primary">No Registration</Badge> >No KYC</Badge
<Badge variant="outline" class="border-bitcoin text-bitcoin">Just Bitcoin</Badge> >
<Badge variant="outline" class="border-primary text-primary text-xs sm:text-sm"
>No Registration</Badge
>
<Badge variant="outline" class="border-bitcoin text-bitcoin text-xs sm:text-sm"
>Just Bitcoin</Badge
>
</div> </div>
</div> </div>
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="flex gap-8"> <div class="flex flex-col gap-4 sm:flex-row sm:gap-6 md:gap-8">
<!-- Buy Bitcoin --> <!-- Buy Bitcoin -->
<Card <Card
class="h-64 w-64 cursor-pointer border-2 border-success/50 bg-success/20 transition-all hover:scale-105 hover:border-success hover:bg-success/30 active:scale-95" class="h-40 w-40 cursor-pointer border-2 border-success/50 bg-success/20 transition-all hover:scale-105 hover:border-success hover:bg-success/30 active:scale-95 sm:h-52 sm:w-52 md:h-64 md:w-64"
@click="handleCashIn" @click="handleCashIn"
> >
<CardContent class="flex h-full flex-col items-center justify-center gap-3 p-6"> <CardContent
<BitcoinIcon :size="64" class="shrink-0" /> class="flex h-full flex-col items-center justify-center gap-2 p-4 sm:gap-3 sm:p-6"
<span class="text-xl font-bold text-success">Buy Bitcoin</span> >
<span class="text-sm text-foreground/70">Insert cash, receive sats</span> <BitcoinIcon :size="48" class="shrink-0 sm:hidden" />
<BitcoinIcon :size="64" class="hidden shrink-0 sm:block" />
<span class="text-lg font-bold text-success sm:text-xl">Buy Bitcoin</span>
<span class="hidden text-sm text-foreground/70 sm:block">Insert cash, receive sats</span>
</CardContent> </CardContent>
</Card> </Card>
<!-- Sell Bitcoin --> <!-- Sell Bitcoin -->
<Card <Card
class="h-64 w-64 cursor-pointer border-2 border-warning/50 bg-warning/20 transition-all hover:scale-105 hover:border-warning hover:bg-warning/30 active:scale-95" class="h-40 w-40 cursor-pointer border-2 border-warning/50 bg-warning/20 transition-all hover:scale-105 hover:border-warning hover:bg-warning/30 active:scale-95 sm:h-52 sm:w-52 md:h-64 md:w-64"
@click="handleCashOut" @click="handleCashOut"
> >
<CardContent class="flex h-full flex-col items-center justify-center gap-3 p-6"> <CardContent
<span class="text-7xl">💵</span> class="flex h-full flex-col items-center justify-center gap-2 p-4 sm:gap-3 sm:p-6"
<span class="text-xl font-bold text-warning">Sell Bitcoin</span> >
<span class="text-sm text-foreground/70">Pay invoice, receive cash</span> <span class="text-5xl sm:text-7xl">💵</span>
<span class="text-lg font-bold text-warning sm:text-xl">Sell Bitcoin</span>
<span class="hidden text-sm text-foreground/70 sm:block">Pay invoice, receive cash</span>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
<!-- Dev QR buttons --> <!-- Dev QR buttons -->
<div v-if="atmStore.debugMode" class="flex items-center gap-4"> <div
v-if="atmStore.debugMode"
class="flex flex-wrap items-center justify-center gap-2 sm:gap-4"
>
<Button <Button
v-if="lndconnectUrl" v-if="lndconnectUrl"
variant="ghost" variant="ghost"
@ -128,12 +142,13 @@ function handleCashOut() {
<!-- Zeus QR fullscreen overlay --> <!-- Zeus QR fullscreen overlay -->
<div <div
v-if="showZeusQR" v-if="showZeusQR"
class="fixed inset-0 z-[100] flex flex-col items-center justify-center gap-4 bg-black/90" class="fixed inset-0 z-[100] flex flex-col items-center justify-center gap-4 bg-black/90 p-4"
@click.self="showZeusQR = false" @click.self="showZeusQR = false"
> >
<p class="text-sm text-white/70">Scan with Zeus to connect to lnd-alice</p> <p class="text-sm text-white/70">Scan with Zeus to connect to lnd-alice</p>
<div class="rounded-2xl bg-white p-4"> <div class="rounded-2xl bg-white p-3 sm:p-4">
<QRCode :value="lndconnectUrl" :size="400" /> <QRCode :value="lndconnectUrl" :size="280" class="sm:hidden" />
<QRCode :value="lndconnectUrl" :size="400" class="hidden sm:block" />
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Button <Button
@ -153,14 +168,15 @@ function handleCashOut() {
<!-- Lightning.Pub nprofile QR fullscreen overlay --> <!-- Lightning.Pub nprofile QR fullscreen overlay -->
<div <div
v-if="showLpQR" v-if="showLpQR"
class="fixed inset-0 z-[100] flex flex-col items-center justify-center gap-4 bg-black/90" class="fixed inset-0 z-[100] flex flex-col items-center justify-center gap-4 bg-black/90 p-4"
@click.self="showLpQR = false" @click.self="showLpQR = false"
> >
<p class="text-sm text-white/70">Lightning.Pub nprofile</p> <p class="text-sm text-white/70">Lightning.Pub nprofile</p>
<div class="rounded-2xl bg-white p-4"> <div class="rounded-2xl bg-white p-3 sm:p-4">
<QRCode :value="lpNprofile" :size="400" /> <QRCode :value="lpNprofile" :size="280" class="sm:hidden" />
<QRCode :value="lpNprofile" :size="400" class="hidden sm:block" />
</div> </div>
<code class="max-w-md truncate text-xs text-white/50">{{ lpNprofile }}</code> <code class="max-w-[90vw] truncate text-xs text-white/50 sm:max-w-md">{{ lpNprofile }}</code>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Button variant="outline" size="sm" class="text-white" @click="copyToClipboard(lpNprofile)"> <Button variant="outline" size="sm" class="text-white" @click="copyToClipboard(lpNprofile)">
{{ copied ? 'Copied!' : 'Copy' }} {{ copied ? 'Copied!' : 'Copy' }}