React + MotionFiltros

Chips de filtro

Filtros en forma de píldora que se activan y desactivan, cada uno con un pequeño check al encenderse.

Ajustes

5

Código

import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Check } from "lucide-react";

const sizeMap = {
  sm: { pad: "px-3 py-1.5", text: "text-xs" },
  md: { pad: "px-4 py-2", text: "text-sm" },
  lg: { pad: "px-5 py-2.5", text: "text-base" },
} as const;

interface FilterChipsProps {
  accent?: string;
  size?: keyof typeof sizeMap;
  options?: string[];
  value?: string[];
  onChange?: (value: string[]) => void;
}

export default function FilterChips({
  accent = "#0071e3",
  size = "md",
  options = ["Diseño", "Código", "Audio", "Vídeo", "3D"],
  value,
  onChange,
}: FilterChipsProps) {
  const [internal, setInternal] = useState<string[]>(options.slice(0, 1));
  const active = value ?? internal;
  const s = sizeMap[size];

  const toggle = (o: string) => {
    const next = active.includes(o) ? active.filter((x) => x !== o) : [...active, o];
    if (value === undefined) setInternal(next);
    onChange?.(next);
  };

  return (
    <div className="flex w-full max-w-md flex-wrap justify-center gap-2">
      {options.map((o) => {
        const on = active.includes(o);
        return (
          <motion.button
            key={o}
            type="button"
            aria-pressed={on}
            whileTap={{ scale: 0.94 }}
            onClick={() => toggle(o)}
            className={`flex items-center gap-1.5 rounded-full border font-medium transition-colors ${s.pad} ${s.text}`}
            style={{
              borderColor: on ? accent : "oklch(1 0 0 / 14%)",
              backgroundColor: on ? `${accent}22` : "transparent",
              color: on ? accent : undefined,
            }}
          >
            <AnimatePresence initial={false}>
              {on && (
                <motion.span
                  initial={{ width: 0, opacity: 0 }}
                  animate={{ width: "auto", opacity: 1 }}
                  exit={{ width: 0, opacity: 0 }}
                  className="overflow-hidden"
                >
                  <Check className="h-3.5 w-3.5" />
                </motion.span>
              )}
            </AnimatePresence>
            {o}
          </motion.button>
        );
      })}
    </div>
  );
}

Prompt para IA

Pégalo en ChatGPT, Claude o Cursor y adaptará el bloque a tu proyecto con estos ajustes, aunque no uses React.

Más en Filtros

Dónde usarlo

Los chips son la forma más rápida de acotar una lista sin abrir ningún menú: tipos de cocina en una web de recetas, tallas en una tienda de ropa, temas en un blog o perfiles en un portal de empleo. Estos chips de filtro permiten elegir varias opciones a la vez, y el check deja claro cuáles están activas.

Funcionan mejor con hasta ocho etiquetas cortas, de una o dos palabras. Si tienes decenas de opciones, pásalas a un desplegable con buscador. El tamaño pequeño va bien dentro de tarjetas y el grande en el móvil, donde el dedo necesita espacio.