Retour au catalogue

Modal Cookie Consent

Modal de consentement cookies avec toggles de preferences.

modal-pagessimple Both Responsive a11y
minimaluniversalstacked
Theme
"use client";

import { useState } from "react";
import { Cookie, X, Shield } from "lucide-react";

interface CookieCategory {
  id: string;
  label: string;
  description: string;
  required: boolean;
  enabled: boolean;
}

const initialCategories: CookieCategory[] = [
  { id: "essential", label: "Essential", description: "Required for core site functionality. Cannot be disabled.", required: true, enabled: true },
  { id: "analytics", label: "Analytics", description: "Help us understand how visitors interact with our site.", required: false, enabled: false },
  { id: "marketing", label: "Marketing", description: "Used to deliver relevant ads and track campaign performance.", required: false, enabled: false },
  { id: "preferences", label: "Preferences", description: "Remember your settings, language, and personalization choices.", required: false, enabled: true },
];

export default function ModalCookieConsent() {
  const [isOpen, setIsOpen] = useState(true);
  const [showPrefs, setShowPrefs] = useState(false);
  const [categories, setCategories] = useState(initialCategories);

  const toggleCategory = (id: string) => {
    setCategories((prev) =>
      prev.map((c) => (c.id === id && !c.required ? { ...c, enabled: !c.enabled } : c))
    );
  };

  const acceptAll = () => {
    setCategories((prev) => prev.map((c) => ({ ...c, enabled: true })));
    setIsOpen(false);
  };

  const savePrefs = () => setIsOpen(false);

  return (
    <div className="min-h-[500px] flex items-center justify-center px-6" style={{ background: "var(--color-background)" }}>
      <button
        onClick={() => { setIsOpen(true); setShowPrefs(false); }}
        className="px-4 py-2 text-sm font-medium rounded-lg"
        style={{ background: "var(--color-background-alt)", color: "var(--color-foreground)", border: "1px solid var(--color-border)" }}
      >
        Cookie settings
      </button>

      {isOpen && (
        <>
          {/* Backdrop */}
          <div
            className="fixed inset-0 z-40 transition-opacity duration-300"
            style={{ background: "rgba(0,0,0,0.5)" }}
            onClick={() => setIsOpen(false)}
          />

          {/* Modal */}
          <div
            className="fixed bottom-0 left-0 right-0 z-50 md:bottom-auto md:top-1/2 md:left-1/2 md:-translate-x-1/2 md:-translate-y-1/2 md:max-w-lg md:rounded-2xl rounded-t-2xl p-6 shadow-2xl"
            style={{ background: "var(--color-background)", border: "1px solid var(--color-border)" }}
          >
            {/* Header */}
            <div className="flex items-start justify-between mb-4">
              <div className="flex items-center gap-3">
                <div
                  className="w-10 h-10 rounded-xl flex items-center justify-center"
                  style={{ background: "color-mix(in srgb, var(--color-accent) 12%, transparent)" }}
                >
                  <Cookie size={20} style={{ color: "var(--color-accent)" }} />
                </div>
                <div>
                  <h3 className="text-base font-semibold" style={{ color: "var(--color-foreground)" }}>Cookie preferences</h3>
                  <p className="text-xs" style={{ color: "var(--color-foreground-muted)" }}>Manage your privacy settings</p>
                </div>
              </div>
              <button onClick={() => setIsOpen(false)} className="p-1">
                <X size={16} style={{ color: "var(--color-foreground-light)" }} />
              </button>
            </div>

            <p className="text-sm leading-relaxed mb-5" style={{ color: "var(--color-foreground-muted)" }}>
              We use cookies to enhance your experience. You can customize your preferences or accept all cookies.
            </p>

            {/* Preference toggles */}
            {showPrefs && (
              <div className="mb-5 flex flex-col gap-2">
                {categories.map((cat) => (
                  <div
                    key={cat.id}
                    className="flex items-center justify-between p-3 rounded-xl"
                    style={{ background: "var(--color-background-alt)" }}
                  >
                    <div className="flex-1 mr-3">
                      <div className="flex items-center gap-1.5">
                        <span className="text-sm font-medium" style={{ color: "var(--color-foreground)" }}>{cat.label}</span>
                        {cat.required && (
                          <Shield size={10} style={{ color: "var(--color-accent)" }} />
                        )}
                      </div>
                      <p className="text-[11px] mt-0.5" style={{ color: "var(--color-foreground-muted)" }}>{cat.description}</p>
                    </div>
                    <button
                      onClick={() => toggleCategory(cat.id)}
                      disabled={cat.required}
                      className="relative w-10 h-5 rounded-full transition-colors duration-200 flex-shrink-0"
                      style={{
                        background: cat.enabled ? "var(--color-accent)" : "var(--color-border)",
                        opacity: cat.required ? 0.7 : 1,
                        cursor: cat.required ? "not-allowed" : "pointer",
                      }}
                    >
                      <div
                        className="absolute top-0.5 w-4 h-4 rounded-full transition-transform duration-200"
                        style={{
                          background: "var(--color-background)",
                          transform: cat.enabled ? "translateX(22px)" : "translateX(2px)",
                        }}
                      />
                    </button>
                  </div>
                ))}
              </div>
            )}

            {/* Actions */}
            <div className="flex items-center gap-3">
              {!showPrefs ? (
                <>
                  <button
                    onClick={() => setShowPrefs(true)}
                    className="flex-1 px-4 py-2.5 text-sm font-medium rounded-lg transition-colors"
                    style={{ background: "var(--color-background-alt)", color: "var(--color-foreground-muted)" }}
                  >
                    Customize
                  </button>
                  <button
                    onClick={acceptAll}
                    className="flex-1 px-4 py-2.5 text-sm font-medium rounded-lg transition-opacity hover:opacity-90"
                    style={{ background: "var(--color-accent)", color: "var(--color-background)" }}
                  >
                    Accept all
                  </button>
                </>
              ) : (
                <>
                  <button
                    onClick={() => setShowPrefs(false)}
                    className="flex-1 px-4 py-2.5 text-sm font-medium rounded-lg"
                    style={{ background: "var(--color-background-alt)", color: "var(--color-foreground-muted)" }}
                  >
                    Back
                  </button>
                  <button
                    onClick={savePrefs}
                    className="flex-1 px-4 py-2.5 text-sm font-medium rounded-lg transition-opacity hover:opacity-90"
                    style={{ background: "var(--color-accent)", color: "var(--color-background)" }}
                  >
                    Save preferences
                  </button>
                </>
              )}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

Avis

Modal Cookie Consent — React Modal-pages Section — Incubator