Files
Andreas Reinhold / reiniandClaude Opus 5 24eb811f34 Teach 2.0's plain CSS in the Boost guidelines and skills
2.0.0 took Tailwind out of the stack, but the texts Boost copies into
every application still taught its utilities: the core guideline said
"built on Tailwind CSS", the always-on material-3 guideline wrote every
rule as `bg-primary`, `type-*`, `rounded-corner-*`, `state-layer` and
`medium:`, and both skills' tables and examples did the same. None of
those classes exists in 2.0's stylesheets, so an agent following the
guideline wrote markup that compiled to nothing. Found moving ReStride
onto 2.0: its CLAUDE.md, and SealShare's, carry these texts.

The guidelines and skills now teach what 2.0 has: component and layout
component props (`gap="space200"`, `hide-from="medium"`), the `md-type-*`,
`md-ink-*` and interaction classes, and `--md-sys-*` tokens in the
application's own CSS, with breakpoints as range media queries.
UPGRADE.md §1, §2 and §4 describe the finished move instead of the
in-between state, and header comments that pointed at the removed
tokens/utilities.css and tailwind.css, or called a component "still
Tailwind", say what is true now.

BoostVocabularyTest runs DesignGuard over the code in every shipped
guideline and skill (fenced Blade and CSS, and each inline class list),
so a text that teaches a class the stylesheets do not define fails the
suite; it finds 268 violations in the texts as 2.0.0 shipped them. The
guard's own table of what it reports is exempt. BoostResourcesTest now
asks the design skill for each breakpoint's prop value and media query
instead of the removed `medium:` variants.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 11:07:54 +02:00

96 lines
3.3 KiB
PHP

<?php
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use NoNameWeb\LivewireMaterial\Support\Layout;
const SKILL_PATH = __DIR__.'/../../resources/boost/skills/livewire-material-development/SKILL.md';
it('gives the skill the frontmatter Boost requires', function () {
$skill = File::get(SKILL_PATH);
expect($skill)
->toStartWith("---\n")
->toContain("\nname: livewire-material-development\n")
->toMatch('/\ndescription: \S.+\n/');
});
it('names every component in the skill', function () {
$skill = File::get(SKILL_PATH);
$undocumented = collect(File::allFiles(__DIR__.'/../../resources/views/components'))
->filter(fn (SplFileInfo $file): bool => str_ends_with($file->getFilename(), '.blade.php'))
->map(fn (SplFileInfo $file): string => Str::of($file->getRelativePathname())
->beforeLast('.blade.php')
->replace(DIRECTORY_SEPARATOR, '.')
->toString())
->reject(fn (string $component): bool => str_contains($skill, "<x-{$component}"))
->values();
expect($undocumented)->toBeEmpty();
});
const GUIDELINE_PATH = __DIR__.'/../../resources/boost/guidelines/material-3.blade.php';
const DESIGN_SKILL_PATH = __DIR__.'/../../resources/boost/skills/material-3-design/SKILL.md';
it('keeps the Material 3 guideline short, verbatim and always on', function () {
$guideline = File::get(GUIDELINE_PATH);
expect($guideline)
->toStartWith("@verbatim\n")
->toContain("\n@endverbatim")
->toContain("\n## Material 3\n");
// Boost inlines every guideline into every session, so this one must stay a page.
expect(substr_count($guideline, "\n"))->toBeLessThan(120);
});
it('gives the design skill the frontmatter Boost requires', function () {
$skill = File::get(DESIGN_SKILL_PATH);
expect($skill)
->toStartWith("---\n")
->toContain("\nname: material-3-design\n")
->toMatch('/\ndescription: \S.+\n/');
});
it('carries every guideline section in the design skill', function () {
preg_match_all('/^### (.+)$/m', File::get(GUIDELINE_PATH), $sections);
preg_match_all('/^## (.+)$/m', File::get(DESIGN_SKILL_PATH), $chapters);
expect($sections[1])->not->toBeEmpty();
expect(array_values(array_diff($sections[1], $chapters[1])))->toBe([]);
});
it('names every token family and breakpoint in the design skill', function () {
$skill = File::get(DESIGN_SKILL_PATH);
$families = collect(File::files(__DIR__.'/../../resources/css/tokens'))
->flatMap(function (SplFileInfo $file): array {
preg_match_all('/--md-(?:sys|ref)-[a-z]+-/', $file->getContents(), $matches);
return $matches[0];
})
->unique()
->values();
expect($families)->not->toBeEmpty();
foreach ($families as $family) {
expect($skill)->toContain($family.'*');
}
foreach (Layout::BREAKPOINTS as $breakpoint => $width) {
if ($width > 0) {
expect($skill)->toContain("`{$breakpoint}` · `@media (width >= {$width}px)`");
}
}
});
it('attributes the design skill to Google', function () {
expect(File::get(DESIGN_SKILL_PATH))
->toContain("\n## Attribution\n")
->toContain('Creative Commons Attribution 4.0')
->toContain('Apache License 2.0');
});