---
title: Tailwind CSS Theme Adapter
product: Form Library
description: Compare a Tailwind CSS HTML form with a SurveyJS form. Change the primary color, light or dark mode, and corner radius to see both forms update together.
framework: React
source: https://surveyjs.io/form-library/examples/tailwind-theme-adapter/reactjs
index: https://surveyjs.io/form-library/examples/overview.md
---

# Tailwind CSS Theme Adapter (React)

This demo compares a native Tailwind CSS form (on the left) with a JSON-defined SurveyJS form styled using the Tailwind CSS v4 theme adapter (on the right). Change the primary color, light/dark appearance, and corner radius above the forms to see both update together. Open the **Code** tab to compare their implementations.

[Tailwind CSS v4 Theme Adapter Documentation](/form-library/documentation/theme-adapters#tailwind-css-v4 (linkStyle))

[Healthcare Demo Built with the Tailwind Adapter](https://healthcare.demos.surveyjs.io/ (linkStyle))

## Files

### `public/index.html`

```html
<link href="https://fonts.googleapis.com/css?family=Open%20Sans:400,600&display=swap" rel="stylesheet">
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Tailwind CSS Theme Adapter</title>
  </head>
  <body>
    <div
      id="tailwind-demo"
      class="tailwind-demo bg-background p-4 font-sans text-foreground sm:p-6"
      data-mode="light"
      data-radius="rounded"
    >
      <div class="mx-auto max-w-6xl">
        <section
          class="theme-controls mb-6 flex flex-col rounded-lg border border-border bg-card p-4"
          aria-label="Customize both forms"
        >
          <div id="themeControls"></div>
        </section>
        <div class="grid items-start gap-6 md:grid-cols-2">
          <section
            class="native-card min-w-0 rounded-lg border border-border bg-card p-4 shadow-sm"
            aria-labelledby="tailwind-native-heading"
          >
            <header class="mb-5">
              <p class="mb-3 text-xs font-semibold uppercase tracking-wide text-primary">
                HTML + Tailwind CSS
              </p>
              <h2 id="tailwind-native-heading" class="text-lg font-semibold leading-7">
                Contact preferences
              </h2>
              <p class="mt-1 text-sm leading-5 text-muted-foreground">
                Keep your profile and updates in sync.
              </p>
            </header>
            <div id="nativeForm"></div>
          </section>
          <section
            class="survey-card flex min-w-0 flex-col rounded-lg border border-border bg-card p-4 shadow-sm"
            aria-labelledby="tailwind-survey-heading"
          >
            <header class="mb-5">
              <p class="mb-3 text-xs font-semibold uppercase tracking-wide text-primary">
                SurveyJS + theme adapter
              </p>
              <h2 id="tailwind-survey-heading" class="text-lg font-semibold leading-7">
                Contact preferences
              </h2>
              <p class="mt-1 text-sm leading-5 text-muted-foreground">
                Keep your profile and updates in sync.
              </p>
            </header>
            <div id="surveyElement"></div>
          </section>
        </div>
      </div>
    </div>
  </body>
</html>
```

### `src/NativeTailwindForm.jsx`

```js
import React from "react";

function NativeTailwindForm() {
  const [values, setValues] = React.useState({
    fullName: "",
    email: "",
    role: "",
    contactMethod: "email",
    updates: [],
    notes: "",
  });
  const [saved, setSaved] = React.useState(false);

  const handleChange = (event) => {
    const { name, value, type, checked } = event.target;
    setValues((current) => ({
      ...current,
      [name]:
        type === "checkbox"
          ? checked
            ? [...current.updates, value]
            : current.updates.filter((item) => item !== value)
          : value,
    }));
    setSaved(false);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    setSaved(true);
  };

  return (
    <form data-native-form onSubmit={handleSubmit} className="native-form flex flex-col gap-4 pt-4">
      <div className="flex flex-col gap-1.5">
        <label htmlFor="tailwind-full-name" className="text-sm font-medium leading-5">
          Full name{" "}
          <span className="text-destructive" aria-hidden="true">
            *
          </span>
        </label>
        <input
          id="tailwind-full-name"
          name="fullName"
          value={values.fullName}
          onChange={handleChange}
          type="text"
          autoComplete="name"
          required
          placeholder="Alex Morgan"
          className="h-9 w-full rounded-md border border-input bg-card px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
        />
      </div>
      <div className="flex flex-col gap-1.5">
        <label htmlFor="tailwind-email" className="text-sm font-medium leading-5">
          Email address{" "}
          <span className="text-destructive" aria-hidden="true">
            *
          </span>
        </label>
        <input
          id="tailwind-email"
          name="email"
          value={values.email}
          onChange={handleChange}
          type="email"
          autoComplete="email"
          required
          placeholder="alex@example.com"
          className="h-9 w-full rounded-md border border-input bg-card px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
        />
      </div>
      <div className="flex flex-col gap-1.5">
        <label htmlFor="tailwind-role" className="text-sm font-medium leading-5">
          Your role
        </label>
        <select
          id="tailwind-role"
          name="role"
          value={values.role}
          onChange={handleChange}
          className="h-9 w-full rounded-md border border-input bg-card px-3 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
        >
          <option value="">Select your role</option>
          <option value="designer">Designer</option>
          <option value="developer">Developer</option>
          <option value="productManager">Product manager</option>
        </select>
      </div>
      <fieldset className="min-w-0">
        <legend className="mb-1.5 text-sm font-medium leading-5">Preferred contact method</legend>
        <div className="flex flex-col gap-7 py-3">
          <label className="flex cursor-pointer items-center gap-2 text-sm leading-5">
            <input
              name="contactMethod"
              type="radio"
              value="email"
              checked={values.contactMethod === "email"}
              onChange={handleChange}
              className="-my-0.5 h-6 w-6 shrink-0 appearance-none rounded-full border border-input bg-card checked:border-[9px] checked:border-primary checked:bg-primary-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
            />
            Email
          </label>
          <label className="flex cursor-pointer items-center gap-2 text-sm leading-5">
            <input
              name="contactMethod"
              type="radio"
              value="phone"
              checked={values.contactMethod === "phone"}
              onChange={handleChange}
              className="-my-0.5 h-6 w-6 shrink-0 appearance-none rounded-full border border-input bg-card checked:border-[9px] checked:border-primary checked:bg-primary-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
            />
            Phone
          </label>
        </div>
      </fieldset>
      <fieldset className="min-w-0">
        <legend className="mb-1.5 text-sm font-medium leading-5">Send me updates about</legend>
        <div className="flex flex-col gap-7 py-3">
          <label className="flex cursor-pointer items-center gap-2 text-sm leading-5">
            <span className="relative -my-0.5 inline-flex h-6 w-6 shrink-0">
              <input
                name="updates"
                type="checkbox"
                value="productNews"
                checked={values.updates.includes("productNews")}
                onChange={handleChange}
                className="peer h-6 w-6 appearance-none rounded-sm border border-input bg-card checked:border-primary checked:bg-primary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
              />
              <svg
                className="pointer-events-none absolute inset-1 h-4 w-4 text-primary-foreground opacity-0 peer-checked:opacity-100"
                viewBox="0 0 16 16"
                fill="none"
                aria-hidden="true"
              >
                <path
                  d="m4 8 2.5 2.5L12 5"
                  stroke="currentColor"
                  strokeWidth="2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                />
              </svg>
            </span>
            Product news
          </label>
          <label className="flex cursor-pointer items-center gap-2 text-sm leading-5">
            <span className="relative -my-0.5 inline-flex h-6 w-6 shrink-0">
              <input
                name="updates"
                type="checkbox"
                value="tutorials"
                checked={values.updates.includes("tutorials")}
                onChange={handleChange}
                className="peer h-6 w-6 appearance-none rounded-sm border border-input bg-card checked:border-primary checked:bg-primary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
              />
              <svg
                className="pointer-events-none absolute inset-1 h-4 w-4 text-primary-foreground opacity-0 peer-checked:opacity-100"
                viewBox="0 0 16 16"
                fill="none"
                aria-hidden="true"
              >
                <path
                  d="m4 8 2.5 2.5L12 5"
                  stroke="currentColor"
                  strokeWidth="2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                />
              </svg>
            </span>
            Tips and tutorials
          </label>
        </div>
      </fieldset>
      <div className="flex flex-col gap-1.5">
        <label htmlFor="tailwind-notes" className="text-sm font-medium leading-5">
          Notes
        </label>
        <textarea
          id="tailwind-notes"
          name="notes"
          value={values.notes}
          onChange={handleChange}
          rows={3}
          placeholder="Anything else we should know?"
          className="h-19 w-full resize-y rounded-md border border-input bg-card px-3 py-2 text-sm leading-5 text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
        ></textarea>
      </div>
      <button
        type="submit"
        className="mt-4 h-13 w-full cursor-pointer rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground hover:bg-primary-hover focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-card"
      >
        Save preferences
      </button>
      <output
        data-native-status
        role="status"
        hidden={!saved}
        className="text-sm text-muted-foreground"
      >
        Preferences saved for this preview.
      </output>
    </form>
  );
}

export default NativeTailwindForm;
```

### `src/tailwind-demo.js`

```js
import { Model } from "survey-core";

const themeControlsJson = {
    widthMode: "responsive",
    showNavigationButtons: false,
    questionTitleLocation: "left",
    pages: [{
        name: "themeSettings",
        questionTitleWidth: "auto",
        elements: [{
            type: "dropdown",
            name: "primary",
            title: "Primary color:",
            defaultValue: "indigo",
            allowClear: false,
            searchEnabled: false,
            width: "250px",
            minWidth: "230px",
            choices: [
                { value: "indigo", text: "Indigo" },
                { value: "blue", text: "Blue" },
                { value: "emerald", text: "Emerald" },
                { value: "rose", text: "Rose" }
            ]
        }, {
            type: "dropdown",
            name: "mode",
            title: "Appearance:",
            defaultValue: "light",
            allowClear: false,
            searchEnabled: false,
            startWithNewLine: false,
            width: "220px",
            minWidth: "200px",
            choices: [
                { value: "light", text: "Light" },
                { value: "dark", text: "Dark" }
            ]
        }, {
            type: "dropdown",
            name: "radius",
            title: "Corner radius:",
            defaultValue: "rounded",
            allowClear: false,
            searchEnabled: false,
            startWithNewLine: false,
            width: "240px",
            minWidth: "220px",
            choices: [
                { value: "square", text: "Square" },
                { value: "rounded", text: "Rounded" },
                { value: "soft", text: "Soft" }
            ]
        }]
    }]
};

export function createThemeControls() {
    const controls = new Model(themeControlsJson);
    const demo = document.getElementById("tailwind-demo");
    const colors = {
        indigo: "#4f46e5",
        blue: "#2563eb",
        emerald: "#047857",
        rose: "#be123c"
    };

    function updateTheme() {
        const color = colors[controls.getValue("primary")];
        if (!color) return;
        demo.style.setProperty("--color-primary", color);
        demo.style.setProperty("--color-ring", color);
        demo.dataset.mode = controls.getValue("mode");
        demo.dataset.radius = controls.getValue("radius");
    }

    controls.onValueChanged.add(updateTheme);
    controls.onCompleting.add((_, options) => { options.allow = false; });
    updateTheme();
    return controls;
}

export function initializeNativeTailwindForm() {
    const form = document.querySelector("#nativeForm form");
    const status = form.querySelector("[data-native-status]");

    form.addEventListener("submit", event => {
        event.preventDefault();
        status.textContent = "Preferences saved for this preview.";
        status.hidden = false;
    });
    form.addEventListener("input", () => { status.hidden = true; });
}
```

### `src/tailwind.css`

```css
/* Precompiled Tailwind CSS utilities for the comparison page and native form components. */
/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
  :root, :host {
    --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
      "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
      "Courier New", monospace;
    --spacing: 0.25rem;
    --container-6xl: 72rem;
    --text-xs: 0.75rem;
    --text-xs--line-height: calc(1 / 0.75);
    --text-sm: 0.875rem;
    --text-sm--line-height: calc(1.25 / 0.875);
    --text-lg: 1.125rem;
    --text-lg--line-height: calc(1.75 / 1.125);
    --font-weight-medium: 500;
    --font-weight-semibold: 600;
    --tracking-wide: 0.025em;
    --radius-sm: 0.25rem;
    --radius-md: 0.375rem;
    --radius-lg: 0.5rem;
    --default-font-family: var(--font-sans);
    --default-mono-font-family: var(--font-mono);
    --color-background: #f9fafb;
    --color-primary: #4f46e5;
    --color-primary-hover: color-mix(in srgb, #4f46e5 85%, black);
    @supports (color: color-mix(in lab, red, red)) {
      --color-primary-hover: color-mix(in srgb, var(--color-primary) 85%, black);
    }
    --color-primary-foreground: #ffffff;
    --color-ring: var(--color-primary);
    --color-card: #ffffff;
    --color-foreground: #111827;
    --color-muted-foreground: #6b7280;
    --color-input: #d1d5db;
    --color-border: #e5e7eb;
    --color-destructive: #dc2626;
  }
}
@layer base {
  *, ::after, ::before, ::backdrop, ::file-selector-button {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    border: 0 solid;
  }
  html, :host {
    line-height: 1.5;
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
    font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
    font-feature-settings: var(--default-font-feature-settings, normal);
    font-variation-settings: var(--default-font-variation-settings, normal);
    -webkit-tap-highlight-color: transparent;
  }
  hr {
    height: 0;
    color: inherit;
    border-top-width: 1px;
  }
  abbr:where([title]) {
    -webkit-text-decoration: underline dotted;
    text-decoration: underline dotted;
  }
  h1, h2, h3, h4, h5, h6 {
    font-size: inherit;
    font-weight: inherit;
  }
  a {
    color: inherit;
    -webkit-text-decoration: inherit;
    text-decoration: inherit;
  }
  b, strong {
    font-weight: bolder;
  }
  code, kbd, samp, pre {
    font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
    font-feature-settings: var(--default-mono-font-feature-settings, normal);
    font-variation-settings: var(--default-mono-font-variation-settings, normal);
    font-size: 1em;
  }
  small {
    font-size: 80%;
  }
  sub, sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
  }
  sub {
    bottom: -0.25em;
  }
  sup {
    top: -0.5em;
  }
  table {
    text-indent: 0;
    border-color: inherit;
    border-collapse: collapse;
  }
  :-moz-focusring {
    outline: auto;
  }
  progress {
    vertical-align: baseline;
  }
  summary {
    display: list-item;
  }
  ol, ul, menu {
    list-style: none;
  }
  img, svg, video, canvas, audio, iframe, embed, object {
    display: block;
    vertical-align: middle;
  }
  img, video {
    max-width: 100%;
    height: auto;
  }
  button, input, select, optgroup, textarea, ::file-selector-button {
    font: inherit;
    font-feature-settings: inherit;
    font-variation-settings: inherit;
    letter-spacing: inherit;
    color: inherit;
    border-radius: 0;
    background-color: transparent;
    opacity: 1;
  }
  :where(select:is([multiple], [size])) optgroup {
    font-weight: bolder;
  }
  :where(select:is([multiple], [size])) optgroup option {
    padding-inline-start: 20px;
  }
  ::file-selector-button {
    margin-inline-end: 4px;
  }
  ::placeholder {
    opacity: 1;
  }
  @supports (not (-webkit-appearance: -apple-pay-button))  or (contain-intrinsic-size: 1px) {
    ::placeholder {
      color: currentcolor;
      @supports (color: color-mix(in lab, red, red)) {
        color: color-mix(in oklab, currentcolor 50%, transparent);
      }
    }
  }
  textarea {
    resize: vertical;
  }
  ::-webkit-search-decoration {
    -webkit-appearance: none;
  }
  ::-webkit-date-and-time-value {
    min-height: 1lh;
    text-align: inherit;
  }
  ::-webkit-datetime-edit {
    display: inline-flex;
  }
  ::-webkit-datetime-edit-fields-wrapper {
    padding: 0;
  }
  ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
    padding-block: 0;
  }
  ::-webkit-calendar-picker-indicator {
    line-height: 1;
  }
  :-moz-ui-invalid {
    box-shadow: none;
  }
  button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
    appearance: button;
  }
  ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
    height: auto;
  }
  [hidden]:where(:not([hidden="until-found"])) {
    display: none !important;
  }
}
@layer utilities {
  .pointer-events-none {
    pointer-events: none;
  }
  .absolute {
    position: absolute;
  }
  .relative {
    position: relative;
  }
  .inset-1 {
    inset: var(--spacing);
  }
  .mx-auto {
    margin-inline: auto;
  }
  .-my-0\.5 {
    margin-block: calc(var(--spacing) * -0.5);
  }
  .mt-1 {
    margin-top: var(--spacing);
  }
  .mt-4 {
    margin-top: calc(var(--spacing) * 4);
  }
  .mb-1\.5 {
    margin-bottom: calc(var(--spacing) * 1.5);
  }
  .mb-3 {
    margin-bottom: calc(var(--spacing) * 3);
  }
  .mb-5 {
    margin-bottom: calc(var(--spacing) * 5);
  }
  .mb-6 {
    margin-bottom: calc(var(--spacing) * 6);
  }
  .flex {
    display: flex;
  }
  .grid {
    display: grid;
  }
  .inline-flex {
    display: inline-flex;
  }
  .h-4 {
    height: calc(var(--spacing) * 4);
  }
  .h-6 {
    height: calc(var(--spacing) * 6);
  }
  .h-9 {
    height: calc(var(--spacing) * 9);
  }
  .h-13 {
    height: calc(var(--spacing) * 13);
  }
  .h-19 {
    height: calc(var(--spacing) * 19);
  }
  .w-4 {
    width: calc(var(--spacing) * 4);
  }
  .w-6 {
    width: calc(var(--spacing) * 6);
  }
  .w-full {
    width: 100%;
  }
  .max-w-6xl {
    max-width: var(--container-6xl);
  }
  .min-w-0 {
    min-width: 0;
  }
  .shrink-0 {
    flex-shrink: 0;
  }
  .cursor-pointer {
    cursor: pointer;
  }
  .resize-y {
    resize: vertical;
  }
  .appearance-none {
    appearance: none;
  }
  .flex-col {
    flex-direction: column;
  }
  .items-center {
    align-items: center;
  }
  .items-start {
    align-items: flex-start;
  }
  .gap-1\.5 {
    gap: calc(var(--spacing) * 1.5);
  }
  .gap-2 {
    gap: calc(var(--spacing) * 2);
  }
  .gap-4 {
    gap: calc(var(--spacing) * 4);
  }
  .gap-6 {
    gap: calc(var(--spacing) * 6);
  }
  .gap-7 {
    gap: calc(var(--spacing) * 7);
  }
  .rounded-full {
    border-radius: calc(infinity * 1px);
  }
  .rounded-lg {
    border-radius: var(--radius-lg);
  }
  .rounded-md {
    border-radius: var(--radius-md);
  }
  .rounded-sm {
    border-radius: var(--radius-sm);
  }
  .border {
    border-style: var(--tw-border-style);
    border-width: 1px;
  }
  .border-border {
    border-color: var(--color-border);
  }
  .border-input {
    border-color: var(--color-input);
  }
  .bg-background {
    background-color: var(--color-background);
  }
  .bg-card {
    background-color: var(--color-card);
  }
  .bg-primary {
    background-color: var(--color-primary);
  }
  .p-4 {
    padding: calc(var(--spacing) * 4);
  }
  .px-3 {
    padding-inline: calc(var(--spacing) * 3);
  }
  .px-4 {
    padding-inline: calc(var(--spacing) * 4);
  }
  .py-2 {
    padding-block: calc(var(--spacing) * 2);
  }
  .py-3 {
    padding-block: calc(var(--spacing) * 3);
  }
  .pt-4 {
    padding-top: calc(var(--spacing) * 4);
  }
  .font-sans {
    font-family: var(--font-sans);
  }
  .text-lg {
    font-size: var(--text-lg);
    line-height: var(--tw-leading, var(--text-lg--line-height));
  }
  .text-sm {
    font-size: var(--text-sm);
    line-height: var(--tw-leading, var(--text-sm--line-height));
  }
  .text-xs {
    font-size: var(--text-xs);
    line-height: var(--tw-leading, var(--text-xs--line-height));
  }
  .leading-5 {
    --tw-leading: calc(var(--spacing) * 5);
    line-height: calc(var(--spacing) * 5);
  }
  .leading-7 {
    --tw-leading: calc(var(--spacing) * 7);
    line-height: calc(var(--spacing) * 7);
  }
  .font-medium {
    --tw-font-weight: var(--font-weight-medium);
    font-weight: var(--font-weight-medium);
  }
  .font-semibold {
    --tw-font-weight: var(--font-weight-semibold);
    font-weight: var(--font-weight-semibold);
  }
  .tracking-wide {
    --tw-tracking: var(--tracking-wide);
    letter-spacing: var(--tracking-wide);
  }
  .text-destructive {
    color: var(--color-destructive);
  }
  .text-foreground {
    color: var(--color-foreground);
  }
  .text-muted-foreground {
    color: var(--color-muted-foreground);
  }
  .text-primary {
    color: var(--color-primary);
  }
  .text-primary-foreground {
    color: var(--color-primary-foreground);
  }
  .uppercase {
    text-transform: uppercase;
  }
  .opacity-0 {
    opacity: 0%;
  }
  .shadow-sm {
    --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .peer-checked\:opacity-100 {
    &:is(:where(.peer):checked ~ *) {
      opacity: 100%;
    }
  }
  .placeholder\:text-muted-foreground {
    &::placeholder {
      color: var(--color-muted-foreground);
    }
  }
  .checked\:border-\[9px\] {
    &:checked {
      border-style: var(--tw-border-style);
      border-width: 9px;
    }
  }
  .checked\:border-primary {
    &:checked {
      border-color: var(--color-primary);
    }
  }
  .checked\:bg-primary {
    &:checked {
      background-color: var(--color-primary);
    }
  }
  .checked\:bg-primary-foreground {
    &:checked {
      background-color: var(--color-primary-foreground);
    }
  }
  .hover\:bg-primary-hover {
    &:hover {
      @media (hover: hover) {
        background-color: var(--color-primary-hover);
      }
    }
  }
  .focus\:ring-2 {
    &:focus {
      --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
      box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
    }
  }
  .focus\:ring-ring {
    &:focus {
      --tw-ring-color: var(--color-ring);
    }
  }
  .focus\:ring-offset-2 {
    &:focus {
      --tw-ring-offset-width: 2px;
      --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
    }
  }
  .focus\:ring-offset-card {
    &:focus {
      --tw-ring-offset-color: var(--color-card);
    }
  }
  .focus\:outline-none {
    &:focus {
      --tw-outline-style: none;
      outline-style: none;
    }
  }
  .sm\:p-6 {
    @media (width >= 40rem) {
      padding: calc(var(--spacing) * 6);
    }
  }
  .md\:grid-cols-2 {
    @media (width >= 48rem) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }
}
@property --tw-border-style {
  syntax: "*";
  inherits: false;
  initial-value: solid;
}
@property --tw-leading {
  syntax: "*";
  inherits: false;
}
@property --tw-font-weight {
  syntax: "*";
  inherits: false;
}
@property --tw-tracking {
  syntax: "*";
  inherits: false;
}
@property --tw-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-inset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-inset-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-inset-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-ring-color {
  syntax: "*";
  inherits: false;
}
@property --tw-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-inset-ring-color {
  syntax: "*";
  inherits: false;
}
@property --tw-inset-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-ring-inset {
  syntax: "*";
  inherits: false;
}
@property --tw-ring-offset-width {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@property --tw-ring-offset-color {
  syntax: "*";
  inherits: false;
  initial-value: #fff;
}
@property --tw-ring-offset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@layer properties {
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
    *, ::before, ::after, ::backdrop {
      --tw-border-style: solid;
      --tw-leading: initial;
      --tw-font-weight: initial;
      --tw-tracking: initial;
      --tw-shadow: 0 0 #0000;
      --tw-shadow-color: initial;
      --tw-shadow-alpha: 100%;
      --tw-inset-shadow: 0 0 #0000;
      --tw-inset-shadow-color: initial;
      --tw-inset-shadow-alpha: 100%;
      --tw-ring-color: initial;
      --tw-ring-shadow: 0 0 #0000;
      --tw-inset-ring-color: initial;
      --tw-inset-ring-shadow: 0 0 #0000;
      --tw-ring-inset: initial;
      --tw-ring-offset-width: 0px;
      --tw-ring-offset-color: #fff;
      --tw-ring-offset-shadow: 0 0 #0000;
    }
  }
}
```

### `src/SurveyComponent.jsx`

```js
import React from "react";
import { Model } from "survey-core";
import { Survey } from "survey-react-ui";
import "survey-core/survey-core.min.css";
import "./index.css";
import { json } from "./json";
import "survey-core/themes/adapters/tailwind4.css";
import "./tailwind.css";
import { createThemeControls } from "./tailwind-demo";

import { createRoot } from "react-dom/client";
import NativeTailwindForm from "./NativeTailwindForm";

const controls = createThemeControls();
createRoot(document.getElementById("themeControls")).render(React.createElement(Survey, { model: controls }));
createRoot(document.getElementById("nativeForm")).render(React.createElement(NativeTailwindForm));
function SurveyComponent() {
    const survey = new Model(json);
    survey.onComplete.add((sender, options) => {
        console.log(JSON.stringify(sender.data, null, 3));
    });
    survey.onCompleting.add((_, options) => {
        options.allow = false;
        survey.notify("Preferences saved for this preview.", "success");
    });
    return (<Survey model={survey} />);
}

export default SurveyComponent;
```

### `src/index.css`

```css
/* Host theme tokens shared by Tailwind utilities and the SurveyJS adapter. */
.tailwind-demo {
    --spacing: 0.25rem;
    --font-sans: ui-sans-serif, system-ui, sans-serif;
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --color-primary: #4f46e5;
    --color-primary-foreground: #ffffff;
    --color-primary-hover: color-mix(in srgb, var(--color-primary) 85%, black);
    --color-ring: var(--color-primary);
    --color-background: #f9fafb;
    --color-card: #ffffff;
    --color-foreground: #111827;
    --color-muted-foreground: #6b7280;
    --color-input: #d1d5db;
    --color-border: #e5e7eb;
    --color-destructive: #dc2626;
    --radius-sm: 0.25rem;
    --radius-md: 0.375rem;
    --radius-lg: 0.5rem;
    box-sizing: border-box;
    min-height: 100vh;
    flex-shrink: 0;
    padding: 24px;
    background: var(--color-background);
    color: var(--color-foreground);
    color-scheme: light;
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    line-height: 1.25rem;
}

.tailwind-demo[data-mode="dark"] {
    --color-background: #030712;
    --color-card: #111827;
    --color-foreground: #f9fafb;
    --color-muted-foreground: #9ca3af;
    --color-input: #4b5563;
    --color-border: #374151;
    --color-destructive: #f87171;
    color-scheme: dark;
}

.tailwind-demo[data-radius="square"] {
    --radius-sm: 0px;
    --radius-md: 0px;
    --radius-lg: 0px;
}

.tailwind-demo[data-radius="soft"] {
    --radius-sm: 0.375rem;
    --radius-md: 0.75rem;
    --radius-lg: 1rem;
}

.tailwind-demo *,
.tailwind-demo *::before,
.tailwind-demo *::after {
    box-sizing: border-box;
}

.tailwind-demo [hidden] {
    display: none;
}

/* Only host layout changes: component styling comes from tailwind4.css. */
.tailwind-demo .sjs-theme-overrides {
    --sjs-element-min-width: 0px;
    --sjs2-layout-component-page-content-area-padding-horizontal: 0px;
    --sjs2-layout-component-survey-footer-padding-horizontal: 0px;
    --sjs2-layout-component-survey-footer-padding-top: 16px;
    --sjs2-layout-component-survey-footer-padding-bottom: 0px;
}

.theme-controls .sjs-theme-overrides {
    --sjs2-layout-component-survey-box-gap-vertical: 0px;
    --sjs2-layout-component-question-box-gap-horizontal: 8px;
}

.tailwind-demo app-root,
.tailwind-demo component-survey,
.tailwind-demo theme-controls {
    display: block;
}

@media (max-width: 600px) {
    .tailwind-demo {
        padding: 16px;
    }
}
```

### `src/index.js`

```js
import React from "react";
import { createRoot } from "react-dom/client";
import SurveyComponent from "./SurveyComponent";

const root = createRoot(document.getElementById("surveyElement"));
root.render(<SurveyComponent />);
```

### `src/json.js`

```js
export const json = {
  "widthMode": "responsive",
  "completeText": "Save preferences",
  "pages": [
    {
      "name": "preferences",
      "elements": [
        {
          "type": "text",
          "name": "fullName",
          "title": "Full name",
          "isRequired": true,
          "autocomplete": "name",
          "placeholder": "Alex Morgan"
        },
        {
          "type": "text",
          "name": "email",
          "title": "Email address",
          "isRequired": true,
          "inputType": "email",
          "autocomplete": "email",
          "placeholder": "alex@example.com"
        },
        {
          "type": "dropdown",
          "name": "role",
          "title": "Your role",
          "placeholder": "Select your role",
          "choices": [
            { "value": "designer", "text": "Designer" },
            { "value": "developer", "text": "Developer" },
            { "value": "productManager", "text": "Product manager" }
          ]
        },
        {
          "type": "radiogroup",
          "name": "contactMethod",
          "title": "Preferred contact method",
          "defaultValue": "email",
          "choices": [
            { "value": "email", "text": "Email" },
            { "value": "phone", "text": "Phone" }
          ]
        },
        {
          "type": "checkbox",
          "name": "updates",
          "title": "Send me updates about",
          "choices": [
            { "value": "productNews", "text": "Product news" },
            { "value": "tutorials", "text": "Tips and tutorials" }
          ]
        },
        {
          "type": "comment",
          "name": "notes",
          "title": "Notes",
          "rows": 3,
          "placeholder": "Anything else we should know?"
        }
      ]
    }
  ]
};
```

### `package.json`

```json
{
  "dependencies": {
    "react": "latest",
    "react-dom": "latest",
    "survey-core": "latest",
    "survey-react-ui": "latest"
  },
  "devDependencies": {
    "react-scripts": "latest"
  }
}
```

## Other Frameworks

- [Angular](https://surveyjs.io/form-library/examples/tailwind-theme-adapter/angular.md)
- [Vue 3](https://surveyjs.io/form-library/examples/tailwind-theme-adapter/vue3js.md)
- [jQuery](https://surveyjs.io/form-library/examples/tailwind-theme-adapter/jquery.md)
- [Vanilla JS](https://surveyjs.io/form-library/examples/tailwind-theme-adapter/vanillajs.md)
