A component that is only markup does not need a class, and creating one for every button produces a directory of files containing nothing but a render.
{{-- resources/views/components/button.blade.php --}}
@props(['variant' => 'primary', 'size' => null])
<button {{ $attributes->merge(['class' => 'btn btn-'.$variant]) }}>
{{ $slot }}
</button>
{{-- <x-button variant="danger" class="mt-2" wire:click="save"> --}}
@props declares the named attributes with defaults, and everything else lands in $attributes — so the component passes through classes, data attributes and event handlers without enumerating them. merge rather than assignment is what lets a call site add a class without replacing the component’s own. The rule that works is: a class when there is logic, anonymous when there is not, and moving between them is a file rename.