Use when reviewing UI for accessibility — WCAG 2.2 AA, keyboard nav, focus, ARIA, contrast, screen-reader semantics — even on 'is this a11y-OK?' or 'mach das barrierefrei'.
The English summary is being prepared. Showing the original description.
Use when building Laravel emails — Mailables, Markdown templates, queued sending, attachments, previews — even when the user says 'send this as an email' without naming Mailables.
The English summary is being prepared. Showing the original description.
See how to installReview the instructions the agent will receive before you install.
Use this skill when building email functionality:
For simple notification emails (one-off messages), see laravel-notifications. Use Mailables when you need full control over the email template.
app/Mail/ for naming, base class, queueing convention, and the templates in resources/views/emails/ for the project's markdown style.php artisan make:mail InvoiceMail --markdown=emails.invoice.ShouldQueue).resources/views/emails/.php artisan make:mail InvoiceMail --markdown=emails.invoice
declare(strict_types=1);
namespace App\Mail;
use App\Models\Invoice;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class InvoiceMail extends Mailable implements ShouldQueue
{
use Queueable;
use SerializesModels;
public function __construct(
private readonly Invoice $invoice,
) {}
public function envelope(): Envelope
{
return new Envelope(
subject: 'Invoice #' . $this->invoice->getNumber(),
replyTo: ['billing@example.com'],
);
}
public function content(): Content
{
return new Content(
markdown: 'emails.invoice',
with: [
'invoice' => $this->invoice,
'url' => route('invoices.show', $this->invoice->getId()),
],
);
}
/** @return array<int, \Illuminate\Mail\Mailables\Attachment> */
public function attachments(): array
{
return [
Attachment::fromPath('/path/to/invoice.pdf')
->as('invoice-' . $this->invoice->getNumber() . '.pdf')
->withMime('application/pdf'),
];
}
}
{{-- resources/views/emails/invoice.blade.php --}}
<x-mail::message>
# Invoice {{ $invoice->getNumber() }}
Thank you for your order. Here is your invoice summary:
<x-mail::table>
| Item | Amount |
|:-----|-------:|
@foreach ($invoice->getItems() as $item)
| {{ $item->getName() }} | {{ $item->getFormattedAmount() }} |
@endforeach
| **Total** | **{{ $invoice->getFormattedTotal() }}** |
</x-mail::table>
<x-mail::button :url="$url">
View Invoice
</x-mail::button>
Thanks,<br>
{{ config('app.name') }}
</x-mail::message>
// Send immediately
Mail::to($user)->send(new InvoiceMail($invoice));
// Queue for background sending (preferred)
Mail::to($user)->queue(new InvoiceMail($invoice));
// Send later
Mail::to($user)->later(now()->addMinutes(10), new InvoiceMail($invoice));
// Multiple recipients
Mail::to($users)
->cc($manager)
->bcc('archive@example.com')
->send(new InvoiceMail($invoice));
// Assert mail was sent
Mail::fake();
// ... trigger action ...
Mail::assertSent(InvoiceMail::class, function (InvoiceMail $mail) use ($user) {
return $mail->hasTo($user->getEmail());
});
Mail::assertNotSent(InvoiceMail::class);
Mail::assertNothingSent();
Mail::assertQueued(InvoiceMail::class);
// routes/web.php (local only)
Route::get('/mail-preview', function () {
$invoice = Invoice::factory()->create();
return new InvoiceMail($invoice);
});
An email is not a web page. Mail clients strip, rewrite, and ignore CSS that
every browser honors, so a template that renders correctly in
/mail-preview tells you nothing about the inbox. Markdown templates (above)
give you a tested baseline for free — this section is what to hold to when a
design forces you off them.
Four requirements, in order of what breaks first:
<table> elements
with role="presentation", a fixed outer width of 600px, and cellpadding
/ cellspacing / border set to 0. display: flex and
display: grid are unsupported or partially supported in the Windows
Outlook family and collapse to a single stacked column.<style> block. Write style="…" on the element.
A <head><style> block is stripped outright by some webmail clients, and
class selectors then match nothing. Use a CSS inliner at build time if the
template is authored with classes — never ship the classes unresolved. Keep
a <style> block only for what cannot be inlined (media queries), and treat
everything in it as optional.v:fill VML fallback in Outlook, so put the color on
bgcolor and treat the image as decoration.The client list worth testing, and what breaks in each:
| Client | What breaks |
|---|---|
| Outlook 2016-2019 / Windows (Word engine) | flex, grid, float, max-width, border-radius, background images, padding on <div>; the strictest target — if it renders, most others do |
| Outlook.com / Outlook 365 web | strips <style> blocks in some views; rewrites class attributes; ignores margin on several elements |
| Gmail web | clips the message past ~102 KB of HTML with a "view entire message" link — anything below the clip, including the unsubscribe link, is not seen; strips <style> when the message is clipped |
| Gmail app (iOS / Android) | no support for embedded <style> on non-Gmail accounts; media queries ignored there, so the mobile layout must be the fluid default |
| Apple Mail / iOS Mail | the most permissive; auto-scales small text and auto-links dates and addresses unless suppressed — a false green if it is the only client you check |
| Dark mode (Apple Mail, Outlook, Gmail) | colors are force-inverted; a logo on a hardcoded white background becomes a white box on dark, and #000 text on a transparent background becomes invisible |
Verify against a real client, not a preview route. /mail-preview renders in
a browser and proves none of the above. Send to real accounts, or use a
rendering service, before the template ships.
ShouldQueue to avoid blocking requests.build() method.Mail::fake() — verify recipients, content, and queuing.ShouldQueue) — synchronous sending blocks the request.Mail::fake() alone — it doesn't render the template. Use Mail::assertSent() with closure.build() method — use envelope(), content(), attachments().No reviews yet. Tell others what you thought.
No login needed. Share how it worked for you, ask a question, or note where it did not run.
No comments yet. Be the first to write one.
Use when reviewing UI for accessibility — WCAG 2.2 AA, keyboard nav, focus, ARIA, contrast, screen-reader semantics — even on 'is this a11y-OK?' or 'mach das barrierefrei'.
The English summary is being prepared. Showing the original description.
Use when defining or auditing the activation event — aha-moment selection, retention correlation, falsifiable definition. Triggers on 'what is our aha moment', 'redefine activation'.
The English summary is being prepared. Showing the original description.
Use when capturing an architectural decision — file naming, next ADR number, Status / Context / Decision / Consequences, index regen; fires even without saying 'ADR'.
The English summary is being prepared. Showing the original description.
Adversarial critique — devil's advocate, stress-test, honest teardown ('poke holes', 'be brutal', 'was hältst du davon'); explicit request only. Routine code or design review → code-review.
The English summary is being prepared. Showing the original description.
Use when reading, creating, or updating agent documentation, module docs, roadmaps, or AGENTS.md. Understands the full .augment/, agents/, and copilot-instructions structure.
The English summary is being prepared. Showing the original description.
Use for an adversarial red-team / blue-team / auditor review of an AI agent's CONFIG + behaviour (rules, skills, MCP, hooks, permissions) — attack-chain → defensive-gap list, not a code audit.
The English summary is being prepared. Showing the original description.