Web Components y Vue
Componentes agnósticos al framework para máxima compatibilidad con cualquier arquitectura.
Web Component (Vanilla JS, PHP, Rails)
Si no utilizas React ni Vue, o estás integrando Cord en un sitio renderizado en el servidor (como Laravel, Django, o WordPress), puedes utilizar el Web Component estándar <cord-quote> (<cord-cotizador> sigue funcionando como alias).
Este componente está compilado bajo los estándares de la plataforma web, lo que significa que el navegador lo entiende de forma nativa sin necesidad de un bundler.
<!-- Importar el módulo ES, con la versión mayor fija -->
<script type="module" src="https://unpkg.com/@flouviahq/elements@2/dist/index.mjs"></script>
<!-- Renderizar el componente nativo -->
<div style="height: 800px;">
<cord-quote
token="9f3b6c2a7e1d4f8b0a5c3e7d1b9f2a46"
min-height="500">
</cord-quote>
</div>
<script>
// Escuchar eventos estándar del DOM
const cotizador = document.querySelector('cord-quote');
cotizador.addEventListener('cord:approved', (e) => {
console.log('Aprobada por', e.detail.signed_by);
});
</script>
Nombres de los eventos: el Web Component emite los mismos eventos que embed.js, con el prefijo cord: (cord:approved, cord:change, cord:error…). Hasta la 2.2 los emitía sin prefijo; esos nombres siguen llegando como alias hasta la 3.0, salvo message, que alcanzaba cada window.addEventListener('message') de tu página: escucha cord:message. La tabla por superficie está en Eventos.
El ejemplo HTML corre sin bundler: <cord-quote> con su estado en vivo y, en otra página, el núcleo headless con tu llave de prueba.
- HTMLEl Web Component desde el CDN y el núcleo headless, sin bundler.
Necesitas el token de una cotización de tu cuenta; el proyecto te lo pide al abrir.
Tu navegador no dejó abrir StackBlitz desde esta página. Los mismos archivos están en la carpeta examples del paquete.
La factura: <cord-invoice>
<cord-invoice> muestra una factura de Cord (la de /i/{token}) en tu sitio: saldo pendiente, estado, partidas, totales y la descarga del PDF. El token es el de la factura hospedada, el mismo que va en el correo que recibe tu cliente.
<cord-invoice token="TOKEN_DE_LA_FACTURA"></cord-invoice>
<script>
document.querySelector('cord-invoice').addEventListener('cord:paid', (e) => {
console.log('Factura pagada', e.detail.folio);
});
</script>
El botón de pago abre la factura hospedada en una ventana de Cord: los datos de pago nunca se capturan dentro del iframe. Mientras la factura sigue abierta, el embed consulta su saldo y emite cord:status_changed cuando cambia y cord:paid cuando queda saldada. Con embed.js, agrega data-cord-document="invoice" al div. Igual que la cotización, sin dominios permitidos en Ajustes › Cotizador embebible los eventos llegan sin datos.
theme, variables y fonts del Appearance API aplican a la factura; rules y layout son del cotizador.
Estado en vivo: state y cord:change
<cord-quote> y <cord-invoice> reducen los eventos del iframe a un estado que puedes leer en cualquier momento con la propiedad state, y emiten cord:change cada vez que cambia. Así dibujas tu propio resumen sin escuchar evento por evento.
<cord-quote token="9f3b6c2a7e1d4f8b0a5c3e7d1b9f2a46"></cord-quote>
<p id="resumen"></p>
<script type="module">
const quote = document.querySelector('cord-quote');
const resumen = document.querySelector('#resumen');
quote.addEventListener('cord:change', (e) => {
const s = e.detail; // igual a quote.state
if (!s.ready) return;
const total = s.total === null ? '' : new Intl.NumberFormat('es-MX', { style: 'currency', currency: s.moneda }).format(s.total);
resumen.textContent = `${s.folio ?? ''} ${s.status ?? ''} ${total}`;
if (s.paid) resumen.textContent += ' · pagada';
});
</script>
| Campo | Contenido |
|---|---|
ready |
El documento terminó de dibujarse. |
status |
sent, viewed, approved, rejected, expired, paid o invoiced; en la factura, open, paid, void o uncollectible. |
folio, moneda, total |
Del documento. El total viaja siempre con su divisa. |
approved, rejected |
Atajos booleanos. |
paid |
Cord registró el pago: estado paid o el evento cord:paid. Una cotización facturada no está pagada por eso. |
invoiced |
Ya se emitió la factura de la cotización, pagada o no. Para entregar o surtir, mira paid. |
signedBy |
Quién aprobó, si aprobó en esta sesión. |
livemode |
false en un documento de prueba; null hasta cord:ready. |
error |
El último cord:error (not_found, expired, frame_blocked o timeout); cord:ready lo limpia. |
lastEvent |
El último evento recibido, por ejemplo cord:approved. |
Sin dominios permitidos en Ajustes › Cotizador embebible, los eventos llegan sin datos del documento y status, folio, moneda y total se quedan en null; livemode y error sí llegan. En React es onChange en <CordCotizador>; en Vue, el evento @change. statechange, onStateChange y @state siguen como alias hasta la 3.0.
Cambiar la apariencia sin recargar
Cambiar appearance (atributo o propiedad), theme o min-height se aplica dentro del iframe sin recargarlo: lo que el cliente estaba escribiendo se conserva. Solo token y base-url cargan otro documento. theme fija el tema sobre la appearance; úsalo para seguir el modo claro u oscuro que elige tu página, que theme: 'auto' no ve porque sigue al sistema operativo.
<cord-quote token="9f3b6c2a7e1d4f8b0a5c3e7d1b9f2a46" theme="light"></cord-quote>
<label><input type="checkbox" id="modo-oscuro"> Modo oscuro</label>
<script type="module">
const quote = document.querySelector('cord-quote');
// El cotizador cambia de tema sin recargar.
document.querySelector('#modo-oscuro').addEventListener('change', (e) => {
quote.setAttribute('theme', e.target.checked ? 'dark' : 'light');
});
// También la appearance completa, como objeto.
quote.appearance = { variables: { colorPrimary: '#0a192f' } };
</script>
En React y Vue pasa una appearance nueva como siempre: el componente la manda al iframe y solo vuelve a montar si cambian token, baseUrl o document. Con mountCotizador(), o con embed.js (window.Cord.mountElement(div) devuelve el mismo controller), llama a controller.update({ appearance, theme, minHeight }). Cord valida la appearance dentro del iframe con las mismas reglas que al montar.
Estilos: ::part
Los dos elementos viven en un Shadow DOM: tus estilos no los rompen y los suyos no tocan tu página. Para personalizarlos desde afuera:
cord-quote::part(frame) {
border-radius: 24px;
box-shadow: 0 12px 40px rgba(10, 25, 47, 0.08);
}
cord-quote::part(skeleton) {
border-radius: 24px;
}
| Parte | Elemento |
|---|---|
root |
El contenedor del elemento. |
skeleton |
El marcador de carga que se ve mientras llega el documento. |
frame |
El <iframe> con el documento de Cord. |
El contenido de adentro del iframe se personaliza con el Appearance API (atributo appearance como JSON, o la propiedad appearance como objeto), no con CSS de tu página. token y base-url cargan otro documento; los demás atributos se aplican sin recargar.
| Atributo | Detalle |
|---|---|
token |
Obligatorio. El token público del documento. |
appearance |
Appearance como JSON. |
theme |
light, dark, auto o flat: fija el tema sobre appearance. |
min-height |
Alto del iframe en píxeles mientras carga (420 por defecto). |
loading |
lazy (por defecto) o eager, para un modal o una pestaña oculta. |
ready-timeout |
Milisegundos para recibir cord:ready antes de cord:error con timeout (10000 por defecto; 0 lo desactiva). |
base-url |
Origen de Cord. Por defecto, https://cordhq.app. |
debug |
Activa la barra de depuración (ver Eventos). |
Formulario fiscal
<cord-fiscal-form> captura los datos fiscales de tu cliente con la misma validación que Cord y se integra a tus formularios HTML. Ver Formulario fiscal.
Vue y Nuxt
El SDK de Cord provee un envoltorio nativo (wrapper) para Vue 3 que maneja la reactividad y los eventos utilizando el paradigma de Vue (@ o v-on).
Instalación
npm install @flouviahq/elements
Uso
Importa el componente <CordCotizador> desde el entrypoint /vue y pásalo a tu template.
<script setup>
import { CordCotizador } from '@flouviahq/elements/vue';
import { ref } from 'vue';
const miToken = ref('9f3b6c2a7e1d4f8b0a5c3e7d1b9f2a46');
function handleApproved(detail) {
console.log('El cliente firmó:', detail.signed_by);
}
// Cord ya abrió la página de pago en su propia ventana: aquí solo se mide.
function handlePay(detail) {
console.log('Pago iniciado', detail.url);
}
function handleError({ code }) {
if (code === 'expired') console.log('La cotización venció');
}
</script>
<template>
<main class="cotizacion-container">
<CordCotizador
:token="miToken"
@approved="handleApproved"
@pay="handlePay"
@error="handleError"
/>
</main>
</template>
Los eventos de Vue son los de React con la convención de Vue (@ready, @change, @error, @item-comment, @status-changed…) y llegan tipados con su payload. Para no importarlo en cada componente, registra CordCotizador y CordInvoice en toda la app con app.use(CordPlugin); Volar los reconoce en las plantillas.
El ejemplo de Vite con Vue registra CordPlugin, escucha @change y @error y cambia el tema sin recargar el iframe.
- Vite + VueEl visor con @change, @error y la apariencia reactiva.
Necesitas el token de una cotización de tu cuenta; el proyecto te lo pide al abrir.
Tu navegador no dejó abrir StackBlitz desde esta página. Los mismos archivos están en la carpeta examples del paquete.
Framer
Si estás construyendo tu sitio web en Framer, puedes agregar Cord Elements como un Code Component arrastrable y soltable.
Crea un nuevo componente de código en Framer y pega lo siguiente:
import { FramerCordCotizador } from '@flouviahq/elements/framer';
export default FramerCordCotizador;
Esto registra los controles de propiedades en el panel derecho de Framer, para que tus diseñadores lo configuren sin tocar código: el token, el documento (cotización o factura), el tema, el color principal, el radio, la fuente, el idioma del aviso que se ve mientras no hay token y el origen de Cord. Como interacciones, el panel ofrece On Ready, On Change, On Error, On Approved y On Paid. Desde código, la prop appearance acepta el objeto completo del Appearance API y gana sobre los controles.
Webflow
A diferencia de Framer (un Code Component real), Webflow no ejecuta componentes de React —
por eso el SDK expone un entrypoint separado (@flouviahq/elements/webflow) pensado para el
editor visual de Webflow: un script de auto-inicialización que no requiere escribir código,
solo atributos personalizados.
- En Webflow, agrega un Div Block donde quieras que aparezca el cotizador.
- En el panel de Settings del elemento, agrega un Custom Attribute:
data-cord-tokencon el token de tu cotización como valor. (Opcionalmente,data-cord-base-urlpara otro origen de Cord,data-cord-min-heightpara el alto del iframe mientras carga,data-cord-theme,data-cord-loadingydata-cord-ready-timeout.) - En Page Settings › Custom Code (o en los ajustes del sitio si quieres que aplique a
todas las páginas), agrega este script antes de
</body>:
<script src="https://unpkg.com/@flouviahq/elements@2/dist/webflow.js"></script>
El script busca automáticamente todos los elementos con data-cord-token en la página (al
cargar, y de nuevo si Webflow los agrega después) y monta el cotizador ahí. Sobre cada div
emite los mismos eventos cord:* que el Web Component (cord:approved, cord:change,
cord:error…): escúchalos con addEventListener desde un bloque Embed de Webflow. La URL
fija la versión mayor (@2), así una versión 3 nunca cambia tu sitio sin que lo decidas.