Unify primary buttons with gradient pill design across the app.
Deploy Ladill QR Plus / deploy (push) Successful in 31s

Add shared btn-primary components and plus icons on create/new actions for a consistent Ladill UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-08 22:49:42 +00:00
co-authored by Cursor
parent d67cb0cd30
commit e8e5f94bb8
24 changed files with 113 additions and 26 deletions
+54
View File
@@ -76,3 +76,57 @@ html.qr-mobile-page body {
width: auto;
}
}
@layer components {
.btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
border-radius: 9999px;
background-image: linear-gradient(to right, rgb(79 70 229), rgb(124 58 237));
padding: 0.5rem 1rem;
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 600;
color: #fff;
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
transition: filter 0.15s ease;
}
.btn-primary:hover {
filter: brightness(0.92);
}
.btn-primary:disabled {
opacity: 0.6;
pointer-events: none;
}
.btn-primary-sm {
padding: 0.375rem 0.75rem;
font-size: 0.75rem;
line-height: 1rem;
}
.btn-primary-lg {
padding: 0.625rem 1.25rem;
}
.btn-fab {
display: inline-flex;
height: 2.75rem;
width: 2.75rem;
align-items: center;
justify-content: center;
border-radius: 9999px;
background-image: linear-gradient(to right, rgb(79 70 229), rgb(124 58 237));
color: #fff;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
transition: filter 0.15s ease;
}
.btn-fab:hover {
filter: brightness(0.92);
}
}
@@ -0,0 +1,17 @@
@props([
'href' => null,
'type' => 'button',
])
@php
$tag = $href ? 'a' : 'button';
@endphp
<{{ $tag }}
@if ($href) href="{{ $href }}" @endif
@if ($tag === 'button') type="{{ $type }}" @endif
{{ $attributes->class(['btn-primary']) }}
>
<svg class="h-4 w-4 shrink-0" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
{{ $slot }}
</{{ $tag }}>
@@ -0,0 +1,16 @@
@props([
'href' => null,
'type' => 'button',
])
@php
$tag = $href ? 'a' : 'button';
@endphp
<{{ $tag }}
@if ($href) href="{{ $href }}" @endif
@if ($tag === 'button') type="{{ $type }}" @endif
{{ $attributes->class(['btn-primary']) }}
>
{{ $slot }}
</{{ $tag }}>
@@ -113,7 +113,7 @@
Cancel
</button>
<button type="submit" :disabled="loading"
class="w-full rounded-xl bg-indigo-600 px-5 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700 disabled:opacity-60 sm:w-auto">
class="btn-primary w-full sm:w-auto">
<span x-text="loading ? 'Processing…' : 'Add credits'">Add credits</span>
</button>
</div>
@@ -30,7 +30,7 @@
class="mt-1 w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm focus:border-indigo-300 focus:bg-white focus:outline-none focus:ring-2 focus:ring-indigo-100">
@error('name')<p class="mt-1 text-xs text-rose-600">{{ $message }}</p>@enderror
</div>
<button class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Generate token</button>
<button class="btn-primary">Generate token</button>
</form>
</div>
@@ -63,7 +63,7 @@
</select>
@error('mailbox_address')<p class="mt-1 text-xs text-rose-600">{{ $message }}</p>@enderror
</div>
<button class="rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-indigo-700">Link mailbox</button>
<button class="btn-primary">Link mailbox</button>
</form>
@endif
</div>
@@ -104,7 +104,7 @@
</label>
</div>
<button class="rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-indigo-700">Save settings</button>
<button class="btn-primary">Save settings</button>
</form>
</div>
@endsection
+1 -1
View File
@@ -23,7 +23,7 @@
<option value="admin">Admin</option>
</select>
</div>
<button class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Send invite</button>
<button class="btn-primary">Send invite</button>
</form>
<p class="mt-2 text-[11px] text-slate-400">Admins can manage mailboxes and the team. Members can manage mailboxes. Invitees join by signing in with that email.</p>
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
<h1 class="text-xl font-semibold tracking-tight text-slate-900">Overview</h1>
<p class="mt-0.5 text-sm text-slate-500">Your mailboxes at a glance.</p>
</div>
<a href="{{ route('email.mailboxes.create') }}" class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-indigo-700">New mailbox</a>
<x-btn.create :href="route('email.mailboxes.create')">New mailbox</x-btn.create>
</div>
@if($error)<div class="mt-4 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">{{ $error }}</div>@endif
@@ -9,7 +9,7 @@
<form method="POST" action="{{ route('email.domains.store') }}" class="mt-5 flex gap-2 rounded-2xl border border-slate-200 bg-white p-2">
@csrf
<input name="domain" required placeholder="yourdomain.com" class="flex-1 rounded-xl border-0 bg-transparent px-3 py-2 text-sm focus:outline-none focus:ring-0">
<button class="rounded-xl bg-indigo-600 px-5 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Add domain</button>
<button class="btn-primary">Add domain</button>
</form>
@if(!empty($domains))
+1 -1
View File
@@ -40,7 +40,7 @@
</div>
<form method="POST" action="{{ route('email.domains.verify', $domain['id']) }}" class="mt-4">
@csrf
<button class="rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700">Verify domain</button>
<button class="btn-primary">Verify domain</button>
<span class="ml-2 text-xs text-slate-400">DNS changes can take time to propagate.</span>
</form>
@else
@@ -86,7 +86,7 @@
</div>
@error('password')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror
<button class="w-full rounded-xl bg-indigo-600 px-4 py-3 text-sm font-semibold text-white hover:bg-indigo-700"
<button class="btn-primary w-full btn-primary-lg"
x-text="free ? 'Create free mailbox' : ('Create mailbox — ' + fmt(price) + '/mo')">Create mailbox</button>
</form>
</div>
@@ -4,7 +4,7 @@
<div class="mx-auto max-w-4xl">
<div class="flex items-center justify-between">
<h1 class="text-xl font-semibold tracking-tight text-slate-900">Mailboxes</h1>
<a href="{{ route('email.mailboxes.create') }}" class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">New mailbox</a>
<x-btn.create :href="route('email.mailboxes.create')">New mailbox</x-btn.create>
</div>
@if($error)<div class="mt-4 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">{{ $error }}</div>@endif
@@ -12,7 +12,7 @@
<div class="mt-6 rounded-2xl border border-dashed border-slate-300 bg-white p-12 text-center">
<p class="text-sm font-semibold text-slate-700">No mailboxes yet</p>
<p class="mx-auto mt-1 max-w-sm text-xs text-slate-400">Verify a domain, then create a mailbox like you@yourdomain.com.</p>
<a href="{{ route('email.mailboxes.create') }}" class="mt-4 inline-block rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Create mailbox</a>
<x-btn.create :href="route('email.mailboxes.create')" class="mt-4">Create mailbox</x-btn.create>
</div>
@else
<div class="mt-6 divide-y divide-slate-100 overflow-hidden rounded-2xl border border-slate-200 bg-white">
@@ -20,7 +20,7 @@
<div class="flex justify-between"><dt class="text-slate-500">IMAP</dt><dd class="font-mono text-slate-800">{{ $host }}:993 (SSL)</dd></div>
<div class="flex justify-between"><dt class="text-slate-500">SMTP</dt><dd class="font-mono text-slate-800">{{ $host }}:587 (STARTTLS)</dd></div>
</dl>
<a href="https://{{ $host }}" class="mt-4 inline-block rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Open Webmail </a>
<a href="https://{{ $host }}" class="mt-4 inline-block btn-primary">Open Webmail </a>
</div>
@php
@@ -46,7 +46,7 @@
</p>
</div>
@if($quotaMb < $maxMb)
<a href="{{ route('email.mailboxes.upgrade', $mailbox['id']) }}" class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Upgrade</a>
<a href="{{ route('email.mailboxes.upgrade', $mailbox['id']) }}" class="btn-primary">Upgrade</a>
@else
<span class="rounded-lg bg-slate-100 px-3 py-2 text-xs font-medium text-slate-500">Top plan</span>
@endif
@@ -44,7 +44,7 @@
</div>
@error('quota_mb')<p class="mt-1 text-xs text-rose-600">{{ $message }}</p>@enderror
</div>
<button class="w-full rounded-xl bg-indigo-600 px-4 py-3 text-sm font-semibold text-white hover:bg-indigo-700"
<button class="btn-primary w-full btn-primary-lg"
x-text="'Upgrade — ' + fmt(price) + '/mo'">Upgrade</button>
</form>
</div>
+1 -1
View File
@@ -96,7 +96,7 @@
<input type="text" x-ref="input" x-model="input" :disabled="loading" placeholder="Message Afia…"
class="flex-1 rounded-xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm text-slate-700 placeholder-slate-400 focus:border-indigo-300 focus:bg-white focus:outline-none focus:ring-2 focus:ring-indigo-100">
<button type="submit" :disabled="loading || !input.trim()"
class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-indigo-600 text-white transition hover:bg-indigo-700 disabled:opacity-40">
class="btn-fab shrink-0 disabled:opacity-40">
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18"/></svg>
</button>
</form>
@@ -33,7 +33,7 @@
<div class="flex items-center justify-center">
<button type="button"
@click="$dispatch('compose-open')"
class="flex h-11 w-11 -translate-y-1 items-center justify-center rounded-full bg-indigo-600 text-white shadow-md ring-4 ring-white transition hover:bg-indigo-700"
class="btn-fab -translate-y-1 ring-4 ring-white shadow-md"
aria-label="Compose">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Z"/></svg>
</button>
+1 -1
View File
@@ -159,7 +159,7 @@
{{-- Save contact button --}}
<div class="px-6 pb-6 pt-2">
<a href="{{ route('qr.public.vcard', $qrCode->short_code) }}"
class="flex w-full items-center justify-center gap-2 rounded-2xl bg-indigo-600 py-3.5 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700">
class="btn-primary w-full">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"/></svg>
Save Contact
</a>
+2 -2
View File
@@ -56,7 +56,7 @@
class="shrink-0">
@csrf
<button type="submit" :disabled="busy"
class="w-full rounded-xl bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-indigo-700 disabled:opacity-60 sm:w-auto">
class="btn-primary w-full sm:w-auto">
<span x-text="busy ? 'Sending…' : 'Share with attendees'">Share with attendees</span>
</button>
</form>
@@ -96,7 +96,7 @@
<div class="flex flex-wrap items-center gap-2">
<span x-show="selected.length" x-cloak class="text-xs font-medium text-slate-500"><span x-text="selected.length"></span> selected</span>
<button type="button" x-show="selected.length" x-cloak @click="printSelected()"
class="rounded-xl bg-indigo-600 px-3.5 py-2 text-xs font-semibold text-white hover:bg-indigo-700 transition">Print selected</button>
class="btn-primary btn-primary-sm">Print selected</button>
<button type="button" x-show="selected.length" x-cloak @click="zplSelected()"
class="rounded-xl border border-slate-200 px-3.5 py-2 text-xs font-semibold text-slate-600 hover:bg-slate-50 transition">ZPL selected</button>
<a href="{{ route('user.qr-codes.badges', $qrCode) }}?auto=1" target="_blank"
+2 -2
View File
@@ -170,7 +170,7 @@
{{-- Desktop submit button --}}
<button type="button"
@click="submitOrTopup($event)"
class="hidden lg:block w-full rounded-xl bg-indigo-600 py-3 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
class="btn-primary w-full hidden lg:block focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
Create QR code
</button>
@@ -192,7 +192,7 @@
</button>
<button type="button"
@click="submitOrTopup($event)"
class="flex flex-1 items-center justify-center gap-2 rounded-xl bg-indigo-600 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700">
class="btn-primary flex-1">
Create
</button>
</div>
+1 -1
View File
@@ -31,7 +31,7 @@
<div class="mt-6">
<button type="button"
@click="goOrTopup($event)"
class="inline-flex items-center gap-2 rounded-xl bg-indigo-600 px-5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-700 transition">
class="btn-primary">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
Create QR code
</button>
+1 -1
View File
@@ -7,7 +7,7 @@
<div class="mt-6 rounded-2xl border border-slate-200 bg-white p-6">
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Current balance</p>
<p class="mt-1 text-3xl font-semibold text-slate-900">{{ $fmt($balanceMinor) }}</p>
<a href="{{ $topupUrl }}" class="mt-4 inline-flex rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Top up wallet</a>
<a href="{{ $topupUrl }}" class="mt-4 btn-primary">Top up wallet</a>
</div>
</div>
</x-user-layout>
@@ -28,7 +28,7 @@
class="mt-1 w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm focus:border-indigo-300 focus:bg-white focus:outline-none focus:ring-2 focus:ring-indigo-100">
@error('name')<p class="mt-1 text-xs text-rose-600">{{ $message }}</p>@enderror
</div>
<button class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Generate token</button>
<button class="btn-primary">Generate token</button>
</form>
</div>
@@ -147,7 +147,7 @@
</div>
</div>
<button type="submit" class="rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-indigo-700">
<button type="submit" class="btn-primary">
Save settings
</button>
</form>
+1 -1
View File
@@ -22,7 +22,7 @@
<option value="admin">Admin</option>
</select>
</div>
<button class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Send invite</button>
<button class="btn-primary">Send invite</button>
</form>
<p class="mt-2 text-[11px] text-slate-400">Admins can manage QR codes and the team. Members can manage QR codes. Invitees join by signing in with that email.</p>
</div>