A Vendor is not "code we didn't write." It's an architectural boundary. For
every third-party library the theme leans on, exactly one file under
assets/vendors/ wraps it — consolidating the library's necessary entrypoints
(its baseline JS, the extensions you actually use, and their styles) behind a
single import. Everything else in the theme imports the Vendor, never the raw
package.
Why route through one file
Real libraries don't ship as one import. You pull the core, then an extension or two, then their separate stylesheets, and every consumer that wants the library has to know that whole shopping list. Centralising it means:
- One source of truth for what "the library" includes. Add the video extension once; every carousel gets it.
- Styles travel with the JS. The Vendor imports the library's CSS as a side effect, so a consumer can't accidentally load the behaviour without the look.
- A seam you can move behind. Swap versions, patch, or re-point the underlying package in one place without touching a single component.
Splide
The carousel library ships a core plus separate extensions and stylesheets. The Vendor pulls the style, the core, and the video extension, and re-exports the two symbols the theme uses:
// vendors/Splide/Splide.js
import './Splide.scss';
import Splide from '@splidejs/splide';
import { Video } from '@splidejs/splide-extension-video'
export { Splide, Video }
A gallery imports { Splide, Video } from here and gets a fully-styled,
video-capable carousel — it never names @splidejs/* itself.
Fancybox
Same shape. The lightbox library's CSS and the two constructors the theme drives are consolidated into one import:
// vendors/Fancybox/Fancybox.js
import './Fancybox.scss'
import { Carousel, Fancybox } from '@fancyapps/ui'
export { Fancybox, Carousel }
The .scss import is load-bearing. Because it's a side effect of importing the
Vendor, pulling Fancybox into a chunk pulls the lightbox styling with it —
which is exactly why the boundary exists. Nothing downstream has to remember to
import the CSS separately.
If you find yourself importing a package like @splidejs/splide or
@fancyapps/ui directly in a component, stop — that's a boundary violation.
Add what you need to the Vendor and import from there. A second entry point to
the same library defeats the single-source guarantee.
Not always this thin
Some Vendors wrap more than a re-export (MapLibre, SnazzyInfoWindow), but the contract is identical: one sanctioned file per library, and the rest of the theme stays ignorant of the raw dependency. When a wrapped subsystem grows its own swappable engines, that's a different, heavier pattern — see Maps: the engine adapter.
Related
- Maps: the engine adapter — the pattern for a complex swappable subsystem, one layer above a plain Vendor.
- The Toolbox — utilities are ours and generic; vendors are the boundary around someone else's code.