Skip to content

Wiring Components Together

Seva components communicate through standard DOM custom events. No framework, state library, or message bus is needed — everything travels through the browser’s built-in event system.

seva-auth seva-event-register seva-cart
│ │ │
├─ seva:authenticated ──────►│ │
│ (user, token) │ │
│ ├─ seva:add-to-cart ────────►│
│ │ (attendees, event) │
│ │ ├─ seva:cart-updated
│ │ │ (itemCount, totalCents)
│ │ ├─ seva:checkout-complete
│ │ │ (registrations)
├─ seva:signed-out ─────────►│ │
│ │ │

The auth component does not automatically inject tokens into sibling components. Your page script must listen for auth events and set the auth-token attribute on components that need it.

<script>
const auth = document.getElementById('auth');
const register = document.getElementById('register');
const cart = document.getElementById('cart');
function propagateAuth() {
const token = auth.getToken();
if (token) {
register.setAttribute('auth-token', token);
cart.setAttribute('auth-token', token);
} else {
register.removeAttribute('auth-token');
cart.removeAttribute('auth-token');
}
}
auth.addEventListener('seva:authenticated', propagateAuth);
auth.addEventListener('seva:signed-out', propagateAuth);
</script>

Web components use Shadow DOM for encapsulation. Attributes are the standard way to pass data into a custom element from the outside. This keeps each component self-contained and avoids hidden coupling.

Important: set attributes before modules load

Section titled “Important: set attributes before modules load”

If you need to set attributes dynamically (e.g., reading tenant-slug from query parameters), use a non-module script placed before the module script tags. Module scripts are deferred by spec, so a regular script runs first:

<!-- This runs synchronously, before module scripts -->
<script>
const params = new URLSearchParams(location.search);
const tenant = params.get('tenant') || 'my-club';
document.getElementById('auth').setAttribute('tenant-slug', tenant);
document.getElementById('register').setAttribute('tenant-slug', tenant);
document.getElementById('cart').setAttribute('tenant-slug', tenant);
</script>
<!-- These are deferred — they see the attributes above -->
<script type="module" src="YOUR_COMPONENTS_URL/seva-auth.js"></script>
<script type="module" src="YOUR_COMPONENTS_URL/seva-event-register.js"></script>
<script type="module" src="YOUR_COMPONENTS_URL/seva-cart.js"></script>

The <seva-cart> component listens on document for seva:add-to-cart events. Because <seva-event-register> fires this event with bubbles: true and composed: true, it reaches the document automatically. You don’t need any glue code for this connection.

To display a cart badge or item count elsewhere on your page, listen for seva:cart-updated:

<span id="cart-badge">Cart: 0 items</span>
<script>
document.addEventListener('seva:cart-updated', (e) => {
const { itemCount, totalCents } = e.detail;
const dollars = (totalCents / 100).toFixed(2);
document.getElementById('cart-badge').textContent =
`Cart: ${itemCount} item${itemCount !== 1 ? 's' : ''} ($${dollars})`;
});
</script>

After a successful checkout (free or paid), <seva-cart> fires seva:checkout-complete. The <seva-event-register> component automatically listens for this event and refreshes its view to show the registered state. If you need to trigger additional page behavior (e.g., redirect or show a thank-you message):

document.addEventListener('seva:checkout-complete', (e) => {
console.log('Registrations confirmed:', e.detail.registrations);
// Redirect, show a message, etc.
});

All three components accept a theme property ('light' or 'dark'). To toggle them together:

function setTheme(dark) {
const theme = dark ? 'dark' : 'light';
document.getElementById('auth').theme = theme;
document.getElementById('register').theme = theme;
document.getElementById('cart').theme = theme;
}

Note: theme is set as a JavaScript property, not an HTML attribute, because it is not reflected.

To programmatically sign a user out (e.g., from your site’s own navigation):

document.getElementById('auth').signOut();
// Then propagate the cleared token
propagateAuth();
EventFired byPayload
seva:authenticatedseva-auth{ user: AuthUser, token: string }
seva:signed-outseva-auth{}
seva:errorseva-auth{ message: string }
seva:view-changedseva-auth, seva-event-register{ view: string }
seva:event-loadedseva-event-register{ event: EventDetail, tickets: TicketDetail[] }
seva:add-to-cartseva-event-register{ eventSlug, eventName, attendees, guestEmail?, hostMemberId? }
seva:registration-errorseva-event-register{ message: string }
seva:registration-declinedseva-event-register{ eventSlug: string }
seva:cart-updatedseva-cart{ itemCount: number, totalCents: number }
seva:checkout-completeseva-cart{ registrations: RegistrationResult[] }
seva:checkout-errorseva-cart{ message: string }