El widget cubre la mayoría de casos out of the box. Este artículo cubre opciones de customización más profundas.
Variables CSS
El widget expone variables CSS para sobrescribir:
| Variable | Default | Propósito |
|---|---|---|
--mm-primary-color | #1a1813 | Color de botón y acento |
--mm-primary-fg | #fff | Texto sobre color primario |
--mm-bg | #fff | Background del widget |
--mm-fg | #1a1813 | Color de texto default |
--mm-muted-fg | #5a5550 | Texto secundario |
--mm-border | #e5e1d8 | Bordes de cards |
--mm-radius | 1rem | Border radius |
--mm-font-family | system-ui | Tipografía |
--mm-spacing | 1rem | Densidad de padding |
--mm-shadow | minimal | Sombra de cards |
Setea en el container div:
<div id="movementors-embed" style="
--mm-primary-color: #c4533c;
--mm-radius: 0;
--mm-font-family: 'Helvetica Neue', sans-serif;
"></div>
O en la stylesheet del sitio:
#movementors-embed {
--mm-primary-color: #c4533c;
--mm-radius: 0;
}
Múltiples widgets por página
Puedes tener múltiples instancias en la misma página. Cada una necesita un container ID único y un data-target correspondiente.
<script src="https://movementors.com/embed.js" data-studio="tu-slug" data-mode="single" data-class="yoga-flow" data-target="mm-featured" async></script>
<div id="mm-featured"></div>
<!-- en otro lugar de la misma página -->
<script src="https://movementors.com/embed.js" data-studio="tu-slug" data-mode="list" data-target="mm-full-list" async></script>
<div id="mm-full-list"></div>
Ambos comparten el mismo script load (browser dedupes), pero renderizan en sus containers.
Filtrar la vista embedded
La página de config del widget permite filtrar lo mostrado. También puedes sobrescribir vía data-*:
| Atributo | Efecto |
|---|---|
data-location | Filtra a un slug de ubicación específico |
data-category | Filtra a slug de categoría (ej. "yoga") |
data-teacher | Filtra a un profesor (slug platform o ID custom) |
data-limit | Cap del número de clases |
data-from | Clases que arrancan después de fecha (ISO) |
data-to | Clases que arrancan antes de fecha |
Ejemplo: widget homepage mostrando solo "próximos 7 días" de yoga en tu ubicación principal:
<script src="https://movementors.com/embed.js"
data-studio="tu-slug"
data-mode="grid"
data-category="yoga"
data-location="main-studio"
data-limit="6"
async></script>
<div id="movementors-embed"></div>
Event tracking
El widget dispara eventos custom que puedes escuchar:
movementors:loaded: DOM del widget listo.movementors:class-viewed: user click una card.movementors:booking-started: user abrió el form de reserva.movementors:booking-completed: reserva exitosa.movementors:booking-failed: reserva falló.
Escucha:
window.addEventListener('movementors:booking-completed', (e) => {
console.log('booking completed', e.detail);
// e.detail contiene: { bookingId, classId, classTitle, amountCents, currency }
});
Útil para:
- Google Analytics: disparar conversion event al completar reserva.
- Facebook Pixel: trackear booking completions.
- Analytics interno: log a tu backend.
Integración CSP
Si tu sitio tiene Content Security Policy, debes permitir MoveMentors en estas directivas:
Content-Security-Policy:
script-src 'self' https://movementors.com;
frame-src https://movementors.com;
connect-src 'self' https://movementors.com https://api.stripe.com;
img-src 'self' https://*.movementors.com https://res.cloudinary.com data:;
El widget usa iframe sandboxing internamente; tu CSP solo debe permitir el framing.
Authentication handoff
El widget soporta "preauth": si un user ya está logueado en tu sitio, puedes pasarlo a MoveMentors sin re-preguntar nombre y email.
<script src="https://movementors.com/embed.js"
data-studio="tu-slug"
data-prefill-name="Jane Doe"
data-prefill-email="jane@example.com"
async></script>
<div id="movementors-embed"></div>
Los valores pre-rellenan el form. El user puede editarlos; esto es conveniencia, no boundary de seguridad. (No asumas que el user está "logueado" solo porque pre-rellenaste; la reserva real igual requiere confirmación de email.)
Server-side rendering
El widget es client-side only. No hay versión SSR que emite HTML para SEO.
Si quieres que tu página de reservas sea indexada por buscadores con contenido completo visible, linkea a las páginas detalle de clase de MoveMentors (que SÍ son SSR) en lugar de depender del widget para SEO.
Para mostrar "próximas 5 clases" visible a crawlers, considera también linkear al directorio MoveMentors; el widget da UI de reserva, el link da SEO juice.
Performance
El bundle pesa ~80KB gzipped. Carga async (atributo async) así no bloquea render de tu página.
Render inicial: ~1-2 segundos en conexión decente.
Después de cargar:
- Interacciones de cards: instantáneas (estado client).
- Apertura del form: instantánea.
- Submit: depende del método de pago (Stripe Checkout agrega ~1 segundo de overhead de redirect).
Si tu sitio es performance-critical, considera lazy-load (montar el widget solo después de interacción). Patrón:
<button id="open-booking">Browse classes</button>
<div id="movementors-embed"></div>
<script>
document.getElementById('open-booking').addEventListener('click', () => {
const s = document.createElement('script');
s.src = 'https://movementors.com/embed.js';
s.dataset.studio = 'tu-slug';
s.async = true;
document.body.appendChild(s);
});
</script>
El widget carga solo on click. Trade-off: pequeño delay extra para users que clickean.
Consideraciones móviles
El widget es responsive. Se adapta:
- Layout single-column bajo 640px.
- Tap targets touch-friendly (44×44 min).
- Form usa teclados móviles nativos (numérico para precios, email para emails).
Si tu sitio tiene layout mobile específico, el widget se puede configurar por layout: usa data-mode distintos en script desktop vs mobile, gateados por media queries CSS.
Preguntas frecuentes
¿Puedo tener versión full white-label con mi logo y sin branding MoveMentors? Customers Enterprise pueden pedir. Default incluye un pequeño footer "powered by MoveMentors".
¿Puedo correr el widget en múltiples dominios? Sí. El widget no whitelista dominios. Cualquiera con tu slug puede embebber.
¿Puedo prevenir que otros embeben mi widget? No actualmente. El widget pulla data pública de clases; cualquiera que sepa tu slug puede embebber. Podríamos agregar domain allow-listing como feature Enterprise.
¿Puedo customizar los campos del form? No actualmente. Los campos son los mismos que el sitio. Customización de campos en roadmap.
¿Reservas del widget cuentan para mi visibilidad / ranking? Sí. Una reserva es una reserva sin importar la fuente. Reservas del embed cuentan para totales de tu studio, reviews agregan igual, etc.
Próximos pasos
- Embed widget: básicos.
- Embed widget no carga: diagnóstico.