Leave a Markdown mail component's classes to the mail theme in the design guard

A mail component is drawn by the mail theme, not by the application's
CSS entry, and the theme's classes share Tailwind's names: ReStride's
`x-mail::sessions` wraps its table in `<div class="table">`, the class
the package's own mail theme styles, and the guard reported it as
Tailwind's `display: table`. A view under a path `mail.markdown.paths`
names (Laravel's `resources/views/vendor/mail` by default) now skips
family (i) and the breakpoint, scale, palette and colour-value checks;
its icon names, the directives in its component tags, forbidColours()
and forbid() are still read. A mail theme stylesheet under that path,
which has to write literal values, is left out of check (iii) and of
the exempt classes, so its `.table` no longer hides `table` in the
application's other views either. The guard's docblock, the README and
the skill's "Testing the design" say so; a fixture test reads the same
mail view and theme with and without the path configured.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Reinhold / reini
2026-09-17 04:18:17 +02:00
co-authored by Claude Opus 5
parent 7a95449c82
commit 0641071a06
8 changed files with 103 additions and 7 deletions
+29
View File
@@ -336,6 +336,35 @@ it('reports a colour utility on the application\'s own theme colour, and a Tailw
]);
});
it('leaves a Markdown mail component\'s classes and its theme to the mail theme, but still reads its icons and directives', function () {
$mail = realpath(GUARD_FIXTURES.'/mail/views');
$table = 'Tailwind display utility `table` compiles to nothing — write the `display` rule in your own CSS';
$icon = 'mail/views/vendor/mail/html/sessions.blade.php:7 unknown Material Symbol `not_a_symbol`';
$directive = 'mail/views/vendor/mail/html/sessions.blade.php:8 Blade directive `@class` inside a component tag, where it does not compile — use `:class="\\Illuminate\\Support\\Arr::toCssClasses([…])"`';
config(['mail.markdown.paths' => [$mail.'/vendor/mail']]);
// The theme's `.table` exempts nothing outside the mail components, and its literals are its own.
expect(fixtureRelative(DesignGuard::scan($mail)->violations()))->toBe([
"mail/views/page.blade.php:1 {$table}",
$icon,
$directive,
]);
// Outside a mail component path the same files read as the application's own: the theme's
// classes as Tailwind, its literals as check (iii)'s, and its `.table` exempting `table`.
config(['mail.markdown.paths' => []]);
expect(fixtureRelative(DesignGuard::scan($mail)->violations()))->toBe([
'mail/views/vendor/mail/html/sessions.blade.php:3 value outside the M3 scale `text-sm` — use one of the `md-type-*` classes (text.css), which set size, line height and tracking together',
'mail/views/vendor/mail/html/sessions.blade.php:4 Tailwind text utility `break-all` compiles to nothing — write the rule in your own CSS',
$icon,
$directive,
'mail/views/vendor/mail/html/themes/default.css:2 literal colour `#3d4852` — use `var(--md-sys-color-*)`',
'mail/views/vendor/mail/html/themes/default.css:3 literal font size `font-size: 14px` — set the whole style with `font: var(--md-sys-typescale-*)` and its `-tracking`, or an `md-type-*` class',
]);
});
it('reports an arbitrary [] value once, whatever utility it modifies, and strips a trailing !', function () {
$violations = fixtureRelative(DesignGuard::scan(realpath(GUARD_FIXTURES.'/tailwind/arbitrary.blade.php'))->violations());
@@ -0,0 +1 @@
<div class="table"></div>
@@ -0,0 +1,8 @@
@props(['sessions', 'url'])
<div class="table">
<table class="sessions text-sm" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr><td class="break-all">{{ $sessions }}</td></tr>
</table>
</div>
<x-mail::button :url="$url" icon="not_a_symbol">Open the week</x-mail::button>
<x-mail::panel @class(['wide'])>A panel</x-mail::panel>
@@ -0,0 +1,4 @@
.table th {
color: #3d4852;
font-size: 14px;
}