capell-app/block-library
Reusable content blocks for Capell with typed definitions, screenshots, and Filament Builder blocks. Ships admin and frontend surfaces, including hero, features, pricing, FAQ, and more for consistent content across packages.
Install and confirm the floor first. composer require capell-app/block-library is the whole install — there are no migrations, no settings, no routes, and no permissions declared in capell.json. Because of that, the two things that actually break a "routine" install are (a) the PHP/Laravel/Filament/Capell-core floors in the package composer.json silently shifting your supported matrix, and (b) the provider not being registered. Check the constraints before you bump, then confirm Capell\BlockLibrary\Providers\BlockLibraryServiceProvider appears in php artisan about / your provider list after clearing cached config and routes.
Where to look first, in order:
Capell\BlockLibrary\Providers\BlockLibraryServiceProvider — this is the real API index. Everything the package exposes (block registration, view namespaces, health check, cache tags) is wired here. Read it before reading any other class.docs/README.md and docs/overview.md — the README is a manifest, not a manual; the overview is where the block-definition model is described.docs/screenshots.json — this is effectively the contract for which surfaces are required to exist (catalog list, editor form, asset settings, plus hero/features/pricing frontend output). Treat it as a smoke-test checklist.ListBlockDefinitionsAction, ResolveBlockDefinitionAction, RegisterBlockDefinitionProviderAction, ValidateDefaultBlockCatalogAction, ListBuilderBlockPickerItemsAction. These five calls are ~90% of day-to-day interaction with the package.packages/block-library/resources/views/blocks/catalog/*.blade.php — the render layer. There are ~19 of these; read two or three to learn the data shape, then stop.First use case: don't build anything. Render an existing catalog block end-to-end. Resolve a BlockDefinitionData for, say, hero, hand its BlockContentContractData to the catalog Blade view, and eyeball the output. That single loop teaches you the definition → variant → content-contract → presentation chain and tells you immediately whether view namespacing is registered correctly. Only after that should you attempt a custom block.
Sanity check the install with the package's own suite rather than hand-testing: vendor/bin/pest packages/block-library/tests --configuration=phpunit.xml. It's cheap and it exercises the catalog validation you'd otherwise discover broken in the editor.
Definitions are registered, not hardcoded. The intended extension point is RegisterBlockDefinitionProviderAction plus one or more definition providers. Any package that wants to contribute blocks registers a provider; nothing reads a static array from config. If you're adding a house block set, that registration is the single line of glue — everything else lives in your own provider.
// Illustrative shape — verify exact signatures against the source before shipping.
app(RegisterBlockDefinitionProviderAction::class)->execute(new MyHouseBlocksProvider());
Resolve at the edge, render in the middle, keep Blade dumb. The clean pattern the package is shaped for:
ListBuilderBlockPickerItemsAction → BuilderBlockPickerItemData collection. Cache this; it's stable per deploy.ResolveBlockDefinitionAction → BlockDefinitionData → PublicBlockPresentationData, then view() with that presentation object.The package deliberately ships PublicBlockPresentationData as a separate type from the authoring-side data. That split is the whole safety story — the moment you pass a definition or a model into a public Blade, you've broken the contract and, per the package's own pitfalls list, you'll leak authoring markers or trigger lazy queries in cached HTML.
Extend by subclassing, not by editing. AbstractCatalogBuilderBlock is the base for all 19+ Filament blocks (HeroBlock, FaqBlock, PricingBlock, ComparisonBlock, CounterBlock, LogosBlock, DividerBlock, ContentBlock, AccordionBlock, CallToActionBlock, StatsBlock, and the rest). A house block is a subclass + a view + a definition entry. Don't fork the vendor Blade files; publish and override, or register your own view path with higher precedence, so a patch release doesn't silently diverge your markup.
Use the contract data objects as typed guardrails. BlockContentContractData, BlockAccessibilityContractData, BlockCompatibilityData, and BlockSettingDefinitionData exist so a block declares what it needs and what it supports rather than assuming it. In practice:
BlockCompatibilityData is where you encode "this block works with theme X / Capell core ≥ Y" — read it in the picker so incompatible blocks don't appear and then explode on save.BlockAccessibilityContractData is your hook for enforcing heading-level, alt-text, and landmark requirements at authoring time. Wire it into the editor form and you stop shipping inaccessible pages instead of auditing for them later.BlockVariantKey + BlockVariantData are the variant model. Key your caches and your fixture snapshots on the variant key — variants are the thing that makes "same block, different output" and therefore the thing that makes cache invalidation go wrong.Caching: one tag, deliberately coarse. The only declared cache tag is block-library. That's a signal — the package expects you to tag block-derived fragments with it and flush it wholesale on release. Don't invent per-block tags on top; you'll get partial invalidation and stale markup on the pages nobody remembered to purge. A deploy that touches Blade views should flush block-library unconditionally.
Fixtures are your regression net. BlockFixtureData and BlockScreenshotData give you a structured way to snapshot each block's rendered output. Run ValidateDefaultBlockCatalogAction in CI — it's the cheapest possible guard against a definition/variant mismatch that would otherwise only surface when an editor opens the Builder.
Health check: Capell\BlockLibrary\Health\BlockLibraryHealthCheck is registered via the provider. Hook it into whatever your ops surface already polls; it typically catches "provider loaded but views/definitions missing," which the generic "package missing" troubleshooting row does not.
$block->children()->get() in a catalog Blade, you've already lost — that fragment will be cached and the query will fire per cache miss.composer update is not safe here. Releases v1.0.7 through v1.0.48 ship with a changelog link and nothing else. A patch bump on a package that defines content structure can change views, validation, or payload shape with zero description. Pin the exact patch (1.0.48, not ^1.0), and read the diff before every bump. Accept the manual-upgrade toil.dd() / dump() a BlockDefinitionData or PublicBlockPresentationData rather than adding debug output to Blade. You'll get the actual property set and stop guessing at field names.vendor:publish, confirm which path wins (namespace registration order in the provider). If your override isn't taking, it's precedence, not caching — but clear the view cache anyway before you spend an hour on it.ContentBlock, HeroBlock, FeaturesBlock are generic names in the Capell\BlockLibrary namespace. If your app defines its own HeroBlock, be explicit with use statements and consider prefixing house blocks. Silent resolution to the wrong class in a Builder form is a nasty, quiet bug.composer.json, composer.local.json, capell.json, docs, docs/screenshots.json, and tests drift independently. If you change the block surface, the screenshots contract and the validation action are the two places that will fail loudly — update them in the same PR as the code.ValidateDefaultBlockCatalogAction — run it in CI and in your deploy smoke test, not at request time.How can I help you explore Laravel packages today?