ibexa/design-system-twig
Twig design system package for Ibexa, offering reusable components, macros, and UI patterns that keep templates consistent. It gives developers a shared set of styling primitives for building and extending Ibexa interfaces.
1. Confirm what you actually installed. The package ships Design System components written in Twig for Symfony. Before wiring anything up, read the repo's README.md and inspect the tree — the important paths are almost always:
templates/ (or src/Resources/views/) — the component templates themselvessrc/ — any Twig extensions, component classes, or bundle registrationpackage.json / webpack.config.js — the CSS/JS build that the components assume.storybook/ — the canonical usage examples for every componentThis package has 0 stars and 0 dependents, so treat the README as the only contract you have. Do not assume an API surface from the name alone.
2. Composer install and bundle registration.
composer require ibexa/design-system-twig
Check whether the repo exposes a Bundle class under src/. If it does, register it in config/bundles.php; if it is template-only, you only need a Twig path. Either way, verify the Twig namespace is registered:
# config/packages/twig.yaml
twig:
paths:
'%kernel.project_dir%/vendor/ibexa/design-system-twig/templates': design_system
3. Get one component rendering before touching styling. Pick the simplest component (typically a button) and render it in isolation:
{# templates/smoke_test.html.twig #}
{% include '@design_system/button/button.html.twig' with {
label: 'Hello',
variant: 'primary'
} only %}
If you get a "template not found" error, the namespace path is wrong. If you get "variable does not exist", the component expects props you haven't passed — go back to the Storybook story for that component; the story file is the real documentation.
4. Load the assets. The components will look broken without the accompanying CSS. Locate the compiled stylesheet the package expects (usually referenced from the README or from a dist/ folder) and include it in your base layout, or import the package's source SCSS into your own build. Confirm this before you start integrating components into real pages, or you will debug phantom markup issues.
First use case: replace one hand-rolled form control (a button or a text input) in an existing template with the Design System equivalent, and diff the rendered HTML. That tells you more about the component's prop contract than any doc in a zero-star repo.
Storybook as the source of truth, not the README. The .storybook/ stories encode every prop combination the component author actually tested. Workflow: open the story → copy the args → translate into Twig with { ... }. When the README and a story disagree, the story wins.
Wrap components in your own thin layer. Never include the raw vendor template from dozens of places. Create project-level wrappers:
{# templates/components/button.html.twig #}
{% include '@design_system/button/button.html.twig' with {
label: label,
variant: variant|default('primary'),
size: size|default('medium'),
attributes: attributes|default({})
} only %}
This gives you one place to change when the package's API shifts (which, at 0 dependents, it will). It also lets you enforce your own defaults and forbid variants you don't want in your design language.
Use include ... only consistently. Without only, every component inherits the full parent context, which makes prop bugs invisible and makes components impossible to reason about in isolation. only forces explicit data flow.
Composition over configuration. Design systems are meant to be nested: a card contains a button, a badge, and an image. Build your page templates out of these compositions rather than reaching for variant flags on the top-level component. This is the main payoff of adopting the package at all.
Attribute passing. Most Symfony-oriented component libraries expect a attributes hash you can splat onto the root element. If the package supports it, pass class and data-* through that channel rather than editing vendor templates — it keeps your overrides upgrade-safe.
Bundle template override for one-off deviations. When a single component needs a project-specific change, override it via Symfony's standard bundle override path (templates/bundles/<BundleName>/...) or a Twig path that shadows the vendor namespace, rather than forking the package. Keep a note of every override — they're your upgrade liabilities.
Form theme integration. If you're using Symfony Forms, the real win is mapping this package's inputs onto a form theme so form_row() emits Design System markup automatically. Do this once, centrally, instead of hand-rendering fields.
Pin the version and vendor-check in CI. Given the adoption signals, run composer why ibexa/design-system-twig and lock the exact version. If you can't stomach a surprise minor-version break, consider a read-only vcs fork you control.
License is NOASSERTION — resolve this first. This is the single biggest blocker. NOASSERTION means GitHub's detector could not identify a recognized license, which may mean a custom license, a missing LICENSE file, or a non-OSI license. Do not ship this in a commercial product until a human has read the actual license file (or confirmed there isn't one — in which case default copyright applies and you have no distribution rights). This is not a formality; it's a legal gate.
Zero stars, zero dependents, zero ecosystem. There is no Stack Overflow corpus, no blog posts, no third-party fixes. When something breaks you are reading Twig source. Budget for that: assume you'll spend more time reading the vendor directory than you would on a mature package. Correspondingly, prefer this package only if you're already in the Ibexa ecosystem and this is the sanctioned path.
Expect no stability guarantee. With no dependents, semver discipline is unverified. Pin exact versions ("ibexa/design-system-twig": "1.2.3" not "^1.2") and read the diff before every bump.
Component context leakage. If a component renders an unexpected value, the cause is almost always a missing only on the include, letting a same-named parent variable shadow the prop you thought you passed. Add only and re-test.
Missing default() filters are a common failure mode. Vendor components often assume every prop is supplied. Defensive pattern: always pass the full prop set from your wrapper and supply |default(...) there, so the vendor template never sees null.
Assets are a separate concern. A very common trap: components render correct markup but look unstyled because the Design System CSS (custom properties, base tokens) was never loaded. If colors and spacing look like an unstyled page, check the CSS bundle before checking the Twig.
Storybook is dev-only. Don't try to make Storybook part of your production build or CI pipeline for a package you merely consume; it requires a full Node toolchain. Use it locally as a reference browser, or read the story files as plain text.
Twig cache is your debugging enemy. After overriding a vendor template, bin/console cache:clear (and in dev, verify twig.cache is off). A stale override silently serving the old template wastes a surprising amount ot time.
Extension points to look for when the package inevitably doesn't do what you need: a Twig Extension class in src/ (custom functions/filters), registered Twig paths (override targets), and any DependencyInjection/Configuration.php (theme/token configuration). If none of these exist, your only lever is template override — plan accordingly.
Debugging technique that works here: render the component in a throwaway route with a minimal, hand-written context. If it works there but not in your page, the bug is context leakage. If it fails in both, the bug is in the props you're passing — compare your with { ... } hash against the Storybook story's args line by line.
How can I help you explore Laravel packages today?