A consent banner that tracks you is a strange thing to have shipped

This is me

This is me

Every Laravel app I shipped ended the same way: bolt on a third-party consent script, hate the result, ship it anyway.

The visual half is what you notice first. The banner arrives with its own design system (a shade of blue that is not yours, a font that is not yours, a modal with its own opinions about corners and shadows) and it sits on top of every page you have. You cannot restyle it, or you can, by overriding a stylesheet you do not control, which lasts until their next release.

The second half is the one that should bother you more. A large share of consent platforms load a remote script, phone home, and assign the visitor an identifier so they can record the consent event. To ask whether you may track someone, they track them. That is not hypocrisy on their part exactly (they need an audit trail, and that is a real requirement for some businesses) but for the apps I build it is a strange trade to accept by default.

Both problems come from the same root: consent is being delivered as a product when for my apps it is a component.

Tokens, not a stylesheet

The design decision that matters is how the banner knows what your app looks like.

The option most component libraries take is configuration: props or a config file for the primary colour, the radius, the font. It works and it is a trap. Every value you expose is a value the consumer has to set, and every value you forgot to expose is a value they cannot. You end up with a growing surface of theme options that still does not cover the case someone actually has.

Wirecookies has no theme options. It reads CSS custom properties (the --wire-* family) that wiremodal and wiretoast already define. The banner does not have a colour; it has whatever --wire-surface currently is:

<html data-wire-theme="studio">

Change that one attribute and the banner, the preferences modal and your toasts all move together, because they are not coordinating; they are reading the same variables. Adding a fourth package to the family requires no changes to the first three.

The cost is a hard dependency: Wirecookies needs a wire* base loaded for the tokens to exist, which is why wiremodal comes in as a dependency and why the import order in your CSS matters. I decided that was cheaper than a config surface that grows forever. It also falls back to its own clean defaults if no theme is set, so it is not broken standing alone.

Pure CSS, no Tailwind, on purpose: a package that requires a specific utility framework is a package that half of Laravel apps cannot use.

One tag, both surfaces, including the one people skip

The component renders the banner and the preferences modal from a single tag. That is not just convenience.

Under the AEPD) the Spanish data protection authority (asking once is not enough. A user has to be able to change or withdraw consent later, as easily as they gave it. Most integrations technically support this and in practice you never build it, because it means a settings page, a route, a link in the footer, and it is nobody's priority once the banner stops appearing in QA.

So the reopen affordance is not optional and not separate. After someone saves, the banner is replaced by a small pinned button that reopens the same modal with the toggles set to whatever they last chose. It ships as part of the tag you already placed, which means the compliant behaviour is the default behaviour and there is nothing to remember to build.

Design rule I keep coming back to: if the correct thing requires extra work, the correct thing does not happen.

localStorage is the whole persistence layer

There is no facade, no methods, no table, no migration. A visitor saves and the browser holds an object:

{ essential: true, analytics: false, marketing: false, functional: false }

plus a wirecookies-saved event so anything on the page can react.

This is a real limitation and worth being explicit about, because it is the one reason to pick something else. There is no server-side audit trail. If your compliance posture requires proving in a dispute that a specific user consented at a specific time, you need a system that records that, and this is not it.

For everything else, which in my experience is most apps, a server-side consent log is a table you will never read, storing personal data you did not previously hold, about a decision the browser already enforces. The dull option is the correct one, and the interesting thing about the persistence layer is that there is nothing interesting about it.

Installation, theming, the copy overrides and the storage shape are on the Wirecookies page. Source on GitHub, package on Packagist.