Ir al contenido

Customization

¿La encuesta se renderiza dentro de un WebView? ¿Podemos personalizar la UI?

En la web, la encuesta no se renderiza dentro de un WebView. El SDK inyecta la encuesta directamente en el DOM de tu página, dentro del elemento contenedor que pasas a form.generate(...). Eso significa que:

  • Es parte del mismo árbol DOM que tu app.
  • Se puede estilar con tu propio CSS e inspeccionar con las DevTools del navegador.
  • Hereda la pila de fuentes, las preferencias de accesibilidad y prefers-reduced-motion de tu página.
  • No hay iframe ni sandbox.

En React Native, la vía recomendada es diferente — mira React Native — y ahí sí se ejecuta dentro de un WebView porque RN no tiene DOM. Es un requisito del host, no una limitación del SDK.

Tienes tres capas independientes de personalización en la web: variables CSS, labels de botones de acción y overrides de las clases CSS generadas para control visual completo.


El SDK distribuye su look-and-feel completo en una sola hoja de estilos en @magicfeedback/native/dist/styles/magicfeedback-default.css. El formulario requiere esta hoja de estilos para renderizar correctamente — cárgala una vez en el punto de entrada de tu app y añade tus overrides debajo.

Elige el snippet que se ajuste a tu integración.

// main.ts / index.ts / app.tsx — tu fichero de entrada
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
// Tus propios overrides van DESPUÉS del import del SDK para ganar empates de specificity.
import "./styles/magicfeedback-overrides.css";

Importa la hoja de estilos desde un componente 'use client' o directamente desde tu root layout. En Pages Router se importa desde _app.tsx.

app/layout.tsx
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
import "./globals.css"; // tus overrides
export default function RootLayout({ children }) {
return (
<html lang="es">
<body>{children}</body>
</html>
);
}
pages/_app.tsx
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
import "../styles/magicfeedback-overrides.css";
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
<link
rel="stylesheet"
href="./node_modules/@magicfeedback/native/dist/styles/magicfeedback-default.css"
/>
<link rel="stylesheet" href="./assets/magicfeedback-overrides.css" />

Si cargas el SDK desde un CDN (p. ej. unpkg o jsDelivr), carga la hoja de estilos correspondiente desde el mismo CDN.

<link
rel="stylesheet"
href="https://unpkg.com/@magicfeedback/native/dist/styles/magicfeedback-default.css"
/>
<link rel="stylesheet" href="/styles/magicfeedback-overrides.css" />

En React Native el SDK corre dentro de un WebView que carga una pequeña página HTML que tú alojas. Añade la hoja de estilos por defecto (y cualquier override) al <head> de esa página. Cargar desde CDN es lo más simple — no hay bundler dentro del WebView.

<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://unpkg.com/@magicfeedback/native/dist/styles/magicfeedback-default.css"
/>
<style>
/* Overrides inline — mantén el WebView en un único fichero autocontenido */
:root {
--mf-primary: #0f766e;
--mf-radius-md: 0.75rem;
}
</style>
</head>
<body>
<div id="survey-root"></div>
<script src="https://unpkg.com/@magicfeedback/native/dist/magicfeedback-sdk.browser.js"></script>
<!-- init + form.generate("survey-root", { ... }) aquí -->
</body>
</html>

Mira React Native para el cableado completo del WebView.

En cualquier integración, asegúrate de cargar la hoja de estilos del SDK antes que tus overrides — si no, tus reglas pueden perder empates de specificity contra las del SDK.

// ✅ orden correcto
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
import "./my-overrides.css";
// ❌ mal — overrides cargados primero
import "./my-overrides.css";
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";

Para apps multi-página donde la encuesta aparece en pocas rutas, puedes diferir la hoja de estilos — pero asegúrate de que esté presente en el DOM antes de ejecutar form.generate(...), o la primera pintura saldrá sin estilos.

async function showFeedback() {
if (!document.getElementById("mf-styles")) {
const link = document.createElement("link");
link.id = "mf-styles";
link.rel = "stylesheet";
link.href = "https://unpkg.com/@magicfeedback/native/dist/styles/magicfeedback-default.css";
document.head.appendChild(link);
await new Promise((resolve) => (link.onload = resolve));
}
const form = magicfeedback.form("APP_ID", "PUBLIC_KEY");
await form.generate("survey-root", { addButton: true });
}

La forma más rápida de aplicar tu marca. Sobrescribe las variables que expone la hoja de estilos por defecto en cualquier sitio de tu propio CSS — después de importar los estilos del SDK.

:root {
--mf-primary: #0f766e;
--mf-primary-hover: #115e59;
--mf-primary-light: #ccfbf1;
--mf-text-primary: #0f172a;
--mf-text-secondary: #475569;
--mf-bg-primary: #ffffff;
--mf-bg-secondary: #f8fafc;
--mf-border: #cbd5e1;
--mf-border-focus: #0f766e;
--mf-radius-md: 0.5rem;
--mf-shadow-md: 0 10px 20px rgba(15, 23, 42, 0.08);
}

Estos son los valores exactos definidos en magicfeedback-default.css. Copia este bloque en tu propia hoja de estilos, cambia solo las variables que te interesen y tendrás una encuesta totalmente con tu marca.

:root {
/* Colores — pastel mínimo */
--mf-primary: #1E293B;
--mf-primary-hover: #0F172A;
--mf-primary-light: #E2E8F0;
--mf-text-primary: #1E293B;
--mf-text-secondary: #475569;
--mf-text-muted: #94A3B8;
--mf-bg-primary: #F8FAFC;
--mf-bg-secondary: #FFFFFF;
--mf-bg-hover: #EEF2F6;
--mf-border: #E2E8F0;
--mf-border-focus: #1E293B;
--mf-success: #16A34A;
--mf-error: #EF4444;
--mf-warning: #F59E0B;
--mf-surface: #FFFFFF;
--mf-surface-alt: #F1F5F9;
--mf-accent: #38BDF8;
/* Espaciado */
--mf-space-xs: 0.25rem;
--mf-space-sm: 0.5rem;
--mf-space-md: 0.75rem;
--mf-space-lg: 1rem;
--mf-space-xl: 1.5rem;
/* Border radius */
--mf-radius-sm: 0.5rem;
--mf-radius-md: 0.75rem;
--mf-radius-lg: 1.25rem;
--mf-radius-full: 9999px;
/* Sombras */
--mf-shadow-sm: 0 1px 2px 0 rgba(15, 23, 42, 0.06);
--mf-shadow-md: 0 8px 20px rgba(15, 23, 42, 0.08);
--mf-shadow-lg: 0 16px 30px rgba(15, 23, 42, 0.12);
--mf-shadow-focus: 0 0 0 3px rgba(30, 41, 59, 0.15);
--mf-shadow-card: 0 18px 40px rgba(15, 23, 42, 0.08);
/* Tipografía */
--mf-font-sans: "Nunito", "Quicksand", "Avenir Next", "Trebuchet MS", sans-serif;
--mf-font-size-sm: 0.875rem;
--mf-font-size-base: 1rem;
--mf-font-size-lg: 1.125rem;
--mf-font-size-xl: 1.25rem;
--mf-line-height: 1.6;
--mf-font-weight-normal: 400;
--mf-font-weight-medium: 500;
--mf-font-weight-semibold: 600;
--mf-font-weight-bold: 700;
--mf-font-weight-extrabold: 800;
/* Transiciones */
--mf-transition: all 0.2s ease;
--mf-transition-fast: all 0.15s ease;
}

Replica tu modo oscuro con un segundo bloque:

@media (prefers-color-scheme: dark) {
:root {
--mf-bg-primary: #0f172a;
--mf-bg-secondary: #111827;
--mf-text-primary: #f8fafc;
--mf-text-secondary: #94a3b8;
--mf-border: #334155;
}
}

Sin JavaScript de por medio. El siguiente render recogerá las variables nuevas.


Pasa los labels de los botones a generate(). Útil para localización o para ajustar el tono sin tocar CSS.

await form.generate("survey-root", {
addButton: true,
sendButtonText: "Enviar feedback",
backButtonText: "Atrás",
nextButtonText: "Siguiente",
startButtonText: "Empezar",
addSuccessScreen: true,
successMessage: "¡Gracias! Hemos recibido tu feedback.",
});

Si quieres control total de la navegación (tus propios botones en tu propio layout), desactiva las acciones integradas:

await form.generate("survey-root", { addButton: false });
document.getElementById("next")?.addEventListener("click", () => form.send());
document.getElementById("back")?.addEventListener("click", () => form.back());

Para retoques visuales más profundos que las variables, sobrescribe las clases directamente desde tu propio CSS. El SDK usa nombres de clase estables (todas con prefijo magicfeedback-), así que es seguro.

A continuación, el mapa completo de clases públicas que trae magicfeedback-default.css, agrupadas por lo que renderizan. Todas las clases llevan el prefijo magicfeedback-.

ClaseQué estila
.magicfeedback-containerWrapper más externo del formulario renderizado.
.magicfeedback-formEl <form> que envuelve preguntas y acciones.
.magicfeedback-questionsWrapper de la lista de preguntas de la página actual.
.magicfeedback-divUna pregunta individual — la unidad para spacing o bordes.
.magicfeedback-labelTítulo principal de una pregunta.
.magicfeedback-sublabelTexto secundario / pista de una pregunta.
.magicfeedback-errorMensaje de error de validación inline.
.magicfeedback-counterIndicador de progreso (p. ej. “2 de 5”).
.magicfeedback-imageImagen renderizada dentro de una pregunta o página.
.magicfeedback-warningTexto de aviso no bloqueante.
ClaseQué estila
.magicfeedback-start-messageContenedor opcional de la pantalla de inicio.
.magicfeedback-start-message-buttonBotón CTA de la pantalla de inicio.
.magicfeedback-info-messagePáginas de info standalone.
.magicfeedback-success-messagePantalla de “gracias” integrada.
ClaseQué estila
.magicfeedback-action-containerBarra que envuelve back / next / submit.
.magicfeedback-submitBotón principal de submit / next.
.magicfeedback-backBotón secundario “atrás”.
.magicfeedback-buttonBotón genérico del SDK (usado en modales).
.magicfeedback-button-primaryVariante primary del botón genérico.
.magicfeedback-skip-containerWrapper del checkbox de “skip”.
Sección titulada «Preguntas de elección (RADIO, MULTIPLECHOICE, BOOLEAN, CONSENT)»
ClaseQué estila
.magicfeedback-radioContenedor de una pregunta RADIO.
.magicfeedback-radio-containerCada opción radio (label + input).
.magicfeedback-checkboxContenedor de una pregunta MULTIPLECHOICE.
.magicfeedback-checkbox-containerCada opción checkbox.
.magicfeedback-boolean-containerContenedor de una pregunta BOOLEAN.
.magicfeedback-boolean-optionCada opción yes/no.
.magicfeedback-consent-containerCheckbox + label de CONSENT.

Preguntas de rating (RATING_STAR, RATING_EMOJI, RATING_NUMBER)

Sección titulada «Preguntas de rating (RATING_STAR, RATING_EMOJI, RATING_NUMBER)»
ClaseQué estila
.magicfeedback-ratingWrapper genérico de rating.
.magicfeedback-rating-containerFila de opciones de rating.
.magicfeedback-rating-placeholderTexto auxiliar bajo un rating (p. ej. “Nada probable”).
.magicfeedback-rating-placeholder-valueEl valor numérico bajo un rating.
.magicfeedback-rating-option-label-containerUna opción de rating emoji.
.magicfeedback-rating-numberWrapper de una pregunta RATING_NUMBER.
.magicfeedback-rating-number-containerFila de opciones numéricas.
.magicfeedback-rating-number-optionUna opción numérica.
.magicfeedback-rating-number-option-label-containerPar label/input dentro de una opción numérica.
.magicfeedback-rating-number-top-placeholderLabel superior de la escala numérica.
.magicfeedback-rating-number-bottom-placeholderLabel inferior de la escala numérica.
.magicfeedback-rating-starWrapper de una pregunta RATING_STAR.
.magicfeedback-rating-star-containerFila de estrellas.
.magicfeedback-rating-star-optionUna estrella.
.magicfeedback-rating-star-selectedEstado visual de la estrella seleccionada.
.rating__starEl glifo de la estrella en sí.

Preguntas con elección por imagen (MULTIPLECHOISE_IMAGE)

Sección titulada «Preguntas con elección por imagen (MULTIPLECHOISE_IMAGE)»
ClaseQué estila
.magicfeedback-multiple-choice-image-optionUna opción de imagen.
.magicfeedback-image-option-label-containerGrupo label/input/imagen.
ClaseQué estila
.magicfeedback-multi-question-matrix-containerWrapper de la matriz.
.magicfeedback-multi-question-matrix-tableLa <table> en sí.
.magicfeedback-multi-question-matrix-row-trUna fila.
.magicfeedback-multi-question-matrix-row-labelLabel izquierdo de la fila.
ClaseQué estila
.magicfeedback-priority-list-headerCabecera de la lista.
.magicfeedback-priority-list-listLa lista ordenada.
.magicfeedback-priority-list-itemUn ítem reordenable.
.magicfeedback-priority-list-item-labelTexto del label del ítem.
.magicfeedback-priority-list-arrowsGrupo de flechas arriba/abajo.
.magicfeedback-priority-list-arrow-upFlecha arriba.
.magicfeedback-priority-list-arrow-downFlecha abajo.
.magicfeedback-priority-list-reorderÁrea de reordenación.
ClaseQué estila
.magicfeedback-point-system-itemUna fila de asignación de puntos.
.magicfeedback-point-system-input-containerWrapper del input de puntos.
.magicfeedback-point-system-totalIndicador de “puntos usados / total”.
Sección titulada «Modal (usado por algunos widgets compuestos)»
ClaseQué estila
.magicfeedback-modal-backdropFondo oscurecido detrás del modal.
.magicfeedback-modalEl panel del modal.
.magicfeedback-modal-actionsZona de acciones dentro del modal.
.magicfeedback-modal-counterContador de progreso dentro del modal.
.magicfeedback-modal-listLista renderizada dentro del modal.
.magicfeedback-modal-rowUna fila dentro de la lista del modal.
.magicfeedback-modal-closeBotón de cierre del modal.
ClaseQué estila
.magicfeedback-visually-hiddenTexto solo para lectores de pantalla.
/* Compactar el spacing de preguntas en modales slim */
.magicfeedback-div {
margin-bottom: 1rem;
}
/* Títulos de pregunta más rotundos */
.magicfeedback-label {
font-weight: 700;
letter-spacing: -0.01em;
}
/* Botón submit en forma de píldora */
.magicfeedback-submit {
border-radius: 9999px;
padding-inline: 1.5rem;
}
/* Ratings emoji cuadrados con un hover lift sutil */
.magicfeedback-rating-option-label-container img {
border-radius: 12px;
transition: transform 0.15s ease;
}
.magicfeedback-rating-option-label-container img:hover {
transform: translateY(-2px) scale(1.05);
}
/* Cambiar el zebra de la matriz por solo bordes */
.magicfeedback-multi-question-matrix-row-tr:nth-child(even) {
background: transparent;
}
.magicfeedback-multi-question-matrix-table td,
.magicfeedback-multi-question-matrix-table th {
border-bottom: 1px solid var(--mf-border);
}

questionFormat cambia entre las dos densidades visuales integradas.

await form.generate("survey-root", {
questionFormat: "slim", // o "standard" (por defecto)
});
  • "standard" — espaciado generoso, inputs grandes. Va bien para encuestas a página completa.
  • "slim" — compacto, encaja dentro de modales, drawers o sidebars.

addSuccessScreen activa la vista de “gracias” integrada. Combínalo con successMessage para localizar, o desactívalo y renderiza la tuya:

await form.generate("survey-root", {
addSuccessScreen: false,
afterSubmitEvent: ({ completed }) => {
if (completed) {
renderMyOwnThankYouView();
}
},
});

Mira Eventos de ciclo de vida para la superficie completa de callbacks.


  • El orden y contenido de las preguntas — viven en el dashboard de MagicFeedback.
  • Los endpoints de la API — seleccionados por init({ env }) entre prod y dev.
  • La lista de tipos de pregunta soportados — mira Referencia de la API → Tipos de pregunta soportados.

Si necesitas un tipo de pregunta, layout o comportamiento que el SDK no expone, habla con tu contacto de MagicFeedback en lugar de parchear el SDK en user-land.