Gå til indhold

Customization

Renderes undersøgelsen inde i en WebView? Kan vi tilpasse UI’et?

På webben renderes undersøgelsen ikke inde i en WebView. SDK’et injicerer undersøgelsen direkte i DOM’en på din side, inde i det containerelement, du sender til form.generate(...). Det betyder, at:

  • Den er en del af samme DOM-træ som din app.
  • Den kan styles med dit eget CSS og inspiceres med browserens DevTools.
  • Den arver din sides font-stack, tilgængelighedsindstillinger og prefers-reduced-motion.
  • Der er ingen iframe og ingen sandbox.

På React Native er vejen anderledes — se React Native — og dér kører undersøgelsen inde i en WebView, fordi RN ikke har en DOM. Det er et hostkrav, ikke en begrænsning i SDK’et.

Du har tre uafhængige tilpasningslag på webben: CSS-variabler, knaplabels og overrides af de genererede CSS-klasser for fuld visuel kontrol.


SDK’et leverer hele sit standardudseende som ét stylesheet på @magicfeedback/native/dist/styles/magicfeedback-default.css. Formularen kræver dette stylesheet for at renderes korrekt — indlæs det én gang i din apps entry og læg dine overrides nedenfor.

Vælg snippet’et der matcher din integration.

Bundler (Vite / Webpack / Rollup / esbuild)

Sektion kaldt “Bundler (Vite / Webpack / Rollup / esbuild)”
// main.ts / index.ts / app.tsx — din entry-fil
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
// Dine egne overrides EFTER SDK-importet, så de vinder specificity-bindinger.
import "./styles/magicfeedback-overrides.css";

Importér stylesheettet fra en 'use client'-komponent eller direkte fra root-layoutet. Pages Router-brugere importerer fra _app.tsx.

app/layout.tsx
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
import "./globals.css"; // dine overrides
export default function RootLayout({ children }) {
return (
<html lang="da">
<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" />

Indlæser du SDK’et fra et CDN (fx unpkg eller jsDelivr), så indlæs det matchende stylesheet fra samme CDN.

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

I React Native kører SDK’et inde i en WebView, som indlæser en lille HTML-side, du hoster. Tilføj standard-stylesheettet (og eventuelle overrides) til den sides <head>. CDN-indlæsning er den enkleste opsætning — der er ingen bundler i WebView’et.

<!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>
/* Inline overrides — hold WebView'et som én selvstændig fil */
: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", { ... }) her -->
</body>
</html>

Se React Native for hele WebView-kablingen.

I enhver integration skal SDK-stylesheettet indlæses før dine overrides — ellers kan dine regler tabe specificity-bindinger til SDK’ets standarder.

// ✅ korrekt rækkefølge
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
import "./my-overrides.css";
// ❌ forkert — overrides indlæst først
import "./my-overrides.css";
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";

I multi-side-apps hvor undersøgelsen kun vises på få ruter, kan du udsætte stylesheettet — men sørg for at det er i DOM’en inden form.generate(...) køres, ellers bliver første rendering uden styles.

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 });
}

Den hurtigste måde at sætte dit brand på. Tilsidesæt variablerne fra standard-stylesheettet i dit eget CSS — efter SDK-importet.

: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);
}

Det er de præcise defaults defineret i magicfeedback-default.css. Kopier blokken ind i dit eget stylesheet, ændr kun det du har brug for, og du har en fuldt brandet undersøgelse.

:root {
/* Farver — minimal pastel */
--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;
/* Spacing */
--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;
/* Skygger */
--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);
/* Typografi */
--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;
/* Transitions */
--mf-transition: all 0.2s ease;
--mf-transition-fast: all 0.15s ease;
}

Match dit dark mode med en anden blok:

@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;
}
}

Ingen JavaScript involveret. Næste rendering henter de nye variabler.


Send labels til generate(). Brug det til lokalisering eller for at matche dit produkts tone uden at røre CSS.

await form.generate("survey-root", {
addButton: true,
sendButtonText: "Send min feedback",
backButtonText: "Tilbage",
nextButtonText: "Næste",
startButtonText: "Lad os starte",
addSuccessScreen: true,
successMessage: "Tak! Vi har lige modtaget din feedback.",
});

Vil du have fuld kontrol over navigation (egne knapper i eget layout), så deaktivér de indbyggede:

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

Hold din Næste-knap synlig på alle sider: sider med ét spørgsmål går som regel selv videre, men ikke når et spørgsmål har det slået fra.

Styr hele surveyen fra dine egne widgets

Sektion kaldt “Styr hele surveyen fra dine egne widgets”

Vil du gå videre og rendere selve spørgsmålene med dine egne komponenter — ikke kun knapperne — så send svarene programmatisk til form.send(). SDK’et springer DOM-scanningen og valideringen over og indsender direkte.

// Render dit UI som du vil. Når brugeren er færdig med en side, så send
// svarene direkte til SDK'et. Tilgængelig fra 2.2.4.
await form.send(
[{ key: "source", value: ["custom-ui"] }], // metadata
[], // metrics
[], // profile
[
{ key: "nps", value: ["9"] },
{ key: "favorite-feature", value: ["Conditional logic"] },
],
);

Livscyklus-hooks udløses stadig, så analytics og UX-kobling fortsætter med at virke. Se form.send(metadata?, metrics?, profile?, answers?) for den fulde kontrakt.


Lag 3 — Tilsidesæt de genererede klasser

Sektion kaldt “Lag 3 — Tilsidesæt de genererede klasser”

For dybere visuelle ændringer end variabler kan du tilsidesætte klasserne direkte fra dit eget CSS. SDK’et bruger stabile klassenavne (alle med præfikset magicfeedback-), så det er sikkert at gøre.

Herunder det fulde kort over offentlige klasser leveret af magicfeedback-default.css, grupperet efter hvad de renderer. Alle klasser har præfikset magicfeedback-.

KlasseHvad den styler
.magicfeedback-containerYderste wrapper omkring formularen.
.magicfeedback-form<form>-elementet med spørgsmål og actions.
.magicfeedback-questionsWrapper omkring spørgsmålslisten på den aktuelle side.
.magicfeedback-divEt enkelt spørgsmål — enheden du normalt rammer for spacing eller borders.
.magicfeedback-labelEt spørgsmåls primære titel.
.magicfeedback-sublabelEt spørgsmåls sekundære tekst / hint.
.magicfeedback-errorInline valideringsfejlbesked.
.magicfeedback-counterProgress-indikator (fx “2 af 5”).
.magicfeedback-imageEt billede renderet i et spørgsmål eller en side.
.magicfeedback-warningIkke-blokerende advarselstekst.
KlasseHvad den styler
.magicfeedback-start-messageDen valgfri intro-skærms container.
.magicfeedback-start-message-buttonCTA-knappen på intro-skærmen.
.magicfeedback-info-messageStandalone info-sider.
.magicfeedback-success-messageDen indbyggede “tak”-skærm.
KlasseHvad den styler
.magicfeedback-action-containerAction-baren omkring back / next / submit.
.magicfeedback-submitPrimær submit / next-knap.
.magicfeedback-backSekundær tilbage-knap.
.magicfeedback-buttonGenerisk SDK-knap (brugt i modaler).
.magicfeedback-button-primaryPrimary-variant af den generiske knap.
.magicfeedback-skip-containerWrapper omkring “skip”-checkboxen.
Sektion kaldt “Valg-spørgsmål (RADIO, MULTIPLECHOICE, BOOLEAN, CONSENT)”
KlasseHvad den styler
.magicfeedback-radioContainer til et RADIO-spørgsmål.
.magicfeedback-radio-containerHver radio-option (label + input).
.magicfeedback-checkboxContainer til et MULTIPLECHOICE-spørgsmål.
.magicfeedback-checkbox-containerHver checkbox-option.
.magicfeedback-boolean-containerContainer til et BOOLEAN-spørgsmål.
.magicfeedback-boolean-optionHver yes/no-option.
.magicfeedback-consent-containerEn CONSENT-checkbox + label.

Rating-spørgsmål (RATING_STAR, RATING_EMOJI, RATING_NUMBER)

Sektion kaldt “Rating-spørgsmål (RATING_STAR, RATING_EMOJI, RATING_NUMBER)”
KlasseHvad den styler
.magicfeedback-ratingGenerisk rating-wrapper.
.magicfeedback-rating-containerRække af rating-options.
.magicfeedback-rating-placeholderHjælpetekst under et rating (fx “Slet ikke sandsynligt”).
.magicfeedback-rating-placeholder-valueNumerisk placeholder under et rating.
.magicfeedback-rating-option-label-containerEn enkelt emoji-rating-option.
.magicfeedback-rating-numberWrapper til et RATING_NUMBER-spørgsmål.
.magicfeedback-rating-number-containerRække af numeriske options.
.magicfeedback-rating-number-optionEn enkelt numerisk option.
.magicfeedback-rating-number-option-label-containerLabel/input-parret inde i en numerisk option.
.magicfeedback-rating-number-top-placeholderTop-label på den numeriske skala.
.magicfeedback-rating-number-bottom-placeholderBund-label på den numeriske skala.
.magicfeedback-rating-starWrapper til et RATING_STAR-spørgsmål.
.magicfeedback-rating-star-containerRække af stjerner.
.magicfeedback-rating-star-optionEn enkelt stjerne.
.magicfeedback-rating-star-selectedVisuel state for valgt stjerne.
.rating__starSelve stjerne-glyffen.

Billed-valg-spørgsmål (MULTIPLECHOISE_IMAGE)

Sektion kaldt “Billed-valg-spørgsmål (MULTIPLECHOISE_IMAGE)”
KlasseHvad den styler
.magicfeedback-multiple-choice-image-optionEn enkelt billed-option.
.magicfeedback-image-option-label-containerLabel/input/billede-gruppering.

Matrix-spørgsmål (MULTI_QUESTION_MATRIX)

Sektion kaldt “Matrix-spørgsmål (MULTI_QUESTION_MATRIX)”
KlasseHvad den styler
.magicfeedback-multi-question-matrix-containerWrapper omkring matrixen.
.magicfeedback-multi-question-matrix-tableSelve <table>’en.
.magicfeedback-multi-question-matrix-row-trEn enkelt række.
.magicfeedback-multi-question-matrix-row-labelRækkens venstre-label.
KlasseHvad den styler
.magicfeedback-priority-list-headerHeader-rækken.
.magicfeedback-priority-list-listDen ordnede liste.
.magicfeedback-priority-list-itemEn enkelt flytbar item.
.magicfeedback-priority-list-item-labelItemets label-tekst.
.magicfeedback-priority-list-arrowsOp/ned-pilegruppen.
.magicfeedback-priority-list-arrow-upOp-pil-knap.
.magicfeedback-priority-list-arrow-downNed-pil-knap.
.magicfeedback-priority-list-reorderReorder-affordance-område.
KlasseHvad den styler
.magicfeedback-point-system-itemEn enkelt point-allokerings-række.
.magicfeedback-point-system-input-containerWrapper omkring points-input’et.
.magicfeedback-point-system-total”Brugte points / total”-indikator.
Sektion kaldt “Modal (brugt af nogle sammensatte widgets)”
KlasseHvad den styler
.magicfeedback-modal-backdropDen nedtonede baggrund bag modalen.
.magicfeedback-modalSelve modal-panelet.
.magicfeedback-modal-actionsAction-området inde i modalen.
.magicfeedback-modal-counterProgress-counter inde i modalen.
.magicfeedback-modal-listListe renderet inde i modalen.
.magicfeedback-modal-rowEn enkelt række inde i modal-listen.
.magicfeedback-modal-closeModal close-knap.
KlasseHvad den styler
.magicfeedback-visually-hiddenTekst kun synlig for skærmlæsere.
/* Kompakt spørgsmåls-spacing inde i slim-modaler */
.magicfeedback-div {
margin-bottom: 1rem;
}
/* Fede spørgsmålstitler */
.magicfeedback-label {
font-weight: 700;
letter-spacing: -0.01em;
}
/* Pille-formet submit-knap */
.magicfeedback-submit {
border-radius: 9999px;
padding-inline: 1.5rem;
}
/* Kvadratiske emoji-ratings med blødt hover-lift */
.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);
}
/* Erstat matrix-striberne med kun border */
.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 skifter mellem de to indbyggede visuelle densiteter.

await form.generate("survey-root", {
questionFormat: "slim", // eller "standard" (default)
});
  • "standard" — generøs spacing, store inputs. Velegnet til full-page-undersøgelser.
  • "slim" — kompakt, passer i modaler, drawers eller sidebars.

På en side med ét spørgsmål indsendes siden af sig selv, når en mulighed vælges, uden at der trykkes Næste. Det gælder:

  • BOOLEAN, CONSENT og RATING_STAR.
  • RADIO, undtagen dens ekstra “Andet”-mulighed (den kræver tekst).
  • RATING_NUMBER og RATING_EMOJI, inklusive deres ekstra mulighed.
  • MULTIPLECHOISE_IMAGE med enkeltvalg.
  • “Spring over”-afkrydsningsfeltet på et LONGTEXT med maxCharacters og en ekstra mulighed med tekst.

Tilgængelig fra 2.2.31.

  • Siden indsendes 300 ms efter valget, så brugeren ser muligheden markeret.
  • Et nyt valg inden for det vindue starter det forfra. Kun det sidste valg indsendes, én gang.
  • Trykkes der Næste inden for vinduet, indsendes siden én gang. Intet indsendes, hvis siden er skiftet (Tilbage, en ny side).
  • Valg med tastaturet virker som et klik eller tryk.

Sider med flere spørgsmål går aldrig automatisk videre. På en side med ét spørgsmål indsender Enter i et TEXT-felt også siden med det samme: det er en eksplicit handling, ikke automatisk videre. Konversationelle (AGENT) surveys viser ét spørgsmål ad gangen, så de samme regler gælder.

Tilgængelig fra 2.2.31.

Et spørgsmål kan have automatisk videre slået fra (autoAdvance: false i dets assets). Det sættes på spørgsmålet, ikke i generate(): åbn spørgsmålet i administrationspanelet og slå Automatisk videre fra. Kontakten vises kun for spørgsmålstyperne ovenfor. Et valg markerer så kun muligheden, og brugeren trykker Næste. Udeladt eller true beholder standardadfærden, og true tilføjer aldrig automatisk videre til andre spørgsmålstyper.


addSuccessScreen tænder/slukker den indbyggede “tak”-visning. Kombiner med successMessage for at lokalisere, eller deaktivér den og render din egen:

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

Se Livscyklus-events for hele callback-overfladen.


Har du brug for en spørgsmålstype, et layout eller en adfærd SDK’et ikke eksponerer, så tal med din MagicFeedback-kontakt frem for at patche SDK’et i userland.