frontend-patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited frontend-patterns (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
@rules/laravel/livewire.mdc — class in app/Livewire, view in resources/views/livewire, extend Livewire\Component; components are slim entry points; delegate business logic to Actions/Services; inject dependencies via boot(), never as method params; Blade stays presentation-only.@rules/laravel/filament.mdc — prefer Filament form/table components for admin UIs; custom Blade+Tailwind needs a registered theme.@rules/laravel/architecture.mdc — keep query/business logic out of views and components.@rules/sql/optimalize.mdc — eager-load to avoid N+1 in loops rendered by Blade.useState/useEffect/useMemo/JSX/Framer Motion/React Query.Compose; do not inherit. Prefer small components with slots over big configurable ones.
{{-- resources/views/components/card.blade.php (anonymous component) --}}
@props(['variant' => 'default'])
<div {{ $attributes->class(['card', 'card-outlined' => $variant === 'outlined']) }}>
{{ $slot }}
</div><x-card variant="outlined">
<x-slot:header>Title</x-slot:header>
Content
</x-card>resources/views/components) for pure presentation.app/View/Components) only when the component needs PHP logic to prepare data.<x-slot:header>) replace prop-drilling content.A "compound" UI is a parent Livewire component holding child Livewire components. Children are independent; pass data down via props and communicate up via events/listeners — never tight coupling.
@foreach ($rows as $row)
<livewire:row-editor :row="$row" :key="$row->id" />
@endforeachAlways set :key on nested components and loop items so Livewire tracks identity across re-renders.
Put state where it belongs. The wrong choice causes either chattiness or lost server state.
#[Computed]) — derived values from properties/DB; cached per request, keeps the view clean.{{-- local-only: no server round-trip --}}
<div x-data="{ open: false }">
<button x-on:click="open = !open">Menu</button>
<nav x-show="open" x-cloak>…</nav>
</div>// derived server state
#[Computed]
public function total(): int
{
return $this->items->sum('price');
}Rule of thumb: if toggling it should hit the database or affect validation, it is Livewire; if it is ephemeral chrome, it is Alpine. Bridge the two with @entangle only when both sides genuinely need the value.
.live only when the server must react to every keystroke; prefer .blur or .debounce.500ms for inputs to cut requests.<input type="search" wire:model.live.debounce.400ms="search"><livewire:report lazy />, or #[Lazy] on the class, to keep the initial response fast.WithPagination; never load full tables into a property.@rules/sql/optimalize.mdc. A relation accessed inside a @foreach without eager loading fires one query per row.$this->orders = Order::with('customer')->latest()->paginate(20);@once + @push('scripts') so repeated components don't duplicate output.Prefer Livewire form objects to keep components slim and validation reusable.
// app/Livewire/Forms/MarketForm.php
class MarketForm extends Form
{
#[Validate('required|string|max:200')]
public string $name = '';
#[Validate('required|string')]
public string $description = '';
}// component
public MarketForm $form;
public function save(CreateMarket $action): void // Action injected via boot() or method DI
{
$this->validate();
$action->handle($this->form->toArray());
$this->reset('form');
}wire:model.blur plus an updated() hook (or per-field $this->validateOnly($field)) validates as the user leaves each field without validating the whole form on every key.messages()/attributes() free of identity-revealing detail per @rules/security/frontend.md.Design all four states, not just the happy path.
{{-- loading --}}
<button wire:click="save" wire:loading.attr="disabled" wire:target="save">
<span wire:loading.remove wire:target="save">Save</span>
<span wire:loading wire:target="save">Saving…</span>
</button>
{{-- empty --}}
@forelse ($orders as $order)
<livewire:order-row :order="$order" :key="$order->id" />
@empty
<x-empty-state title="No orders yet" />
@endforelse
{{-- offline --}}
<div wire:offline class="banner-warning">You are offline — changes will retry.</div>session()->flash() + a role="alert" region, or a validation message; never swallow exceptions in the component. Delegate the actual work to an Action that can throw, and catch only to present a safe message.wire:loading placeholders for lazy/deferred components so layout doesn't jump.Render meaningful HTML server-side first; layer Alpine for interactivity so the page is useful before JS runs and degrades gracefully if it doesn't.
<details x-data x-bind:open="open"> {{-- works without JS via native <details>; Alpine enhances --}}
<summary>Filters</summary>
…
</details>Keep Alpine logic small and inline; if it grows beyond a few expressions, move the state into a Livewire component.
wire:key/:key.wire:model strategy minimizes requests; no .live where .blur/.debounce suffices.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.