npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@techify/ui

v1.1.0

Published

Componentes React com Inline Reactions para formulários e botões

Readme

@techify/ui

Version License React TypeScript

Componentes React com Inline Reactions para feedback visual elegante

Início RápidoComponentesPersonalizaçãoAPI


✨ Características

  • 🎭 Estados visuais claros: idle, loading, success, error
  • 🎬 Animações suaves com Framer Motion
  • 🎨 Totalmente personalizável - estilo shadcn/ui
  • 🔄 Múltiplos modos de loading: skeleton, spinner, blur, pulse, dots, bars
  • 🎛️ Provider global para configuração centralizada
  • Acessível com suporte completo a ARIA
  • 📦 Tree-shakeable - importe apenas o que precisa
  • 🌙 Dark mode via CSS variables
  • 🔧 TypeScript - tipagem completa
  • 🎯 Tamanhos e variantes configuráveis

📦 Instalação

# pnpm (recomendado)
pnpm add @techify/ui framer-motion

# npm
npm install @techify/ui framer-motion

# yarn
yarn add @techify/ui framer-motion

🚀 Início Rápido

1. Configure o Provider (opcional, mas recomendado)

import { TechifyUIProvider } from '@techify/ui';
import '@techify/ui/styles.css';

function App() {
  return (
    <TechifyUIProvider
      config={{
        loading: { mode: 'spinner' },
        defaults: { size: 'md', variant: 'primary' },
      }}
    >
      <YourApp />
    </TechifyUIProvider>
  );
}

2. Use os componentes

import { FormWithInlineReaction, useInlineReaction, ContactFormSkeleton } from '@techify/ui';

function ContactForm() {
  const { status, setLoading, setSuccess, setError, reset } = useInlineReaction();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading();
    
    try {
      await submitContactForm();
      setSuccess();
    } catch {
      setError();
    }
  };

  return (
    <FormWithInlineReaction
      status={status}
      onSubmit={handleSubmit}
      onReset={reset}
      loadingMode="spinner" // ou "skeleton", "blur", "pulse"
      loadingSkeleton={<ContactFormSkeleton />}
      successMessage="Mensagem enviada!"
      errorMessage="Erro ao enviar"
    >
      <input type="text" name="name" placeholder="Nome" />
      <input type="email" name="email" placeholder="Email" />
      <button type="submit">Enviar</button>
    </FormWithInlineReaction>
  );
}

📚 Componentes

FormWithInlineReaction

Formulário com transições suaves entre estados.

import { FormWithInlineReaction, useInlineReaction, FormSkeleton } from '@techify/ui';

function MyForm() {
  const { status, setLoading, setSuccess, setError, reset } = useInlineReaction();

  return (
    <FormWithInlineReaction
      status={status}
      onSubmit={handleSubmit}
      onReset={reset}
      
      // Mensagens
      successMessage="Formulário enviado com sucesso!"
      errorMessage="Erro ao enviar. Tente novamente."
      resetButtonText="Voltar ao formulário"
      
      // Modo de loading
      loadingMode="skeleton" // "skeleton" | "spinner" | "blur" | "pulse" | "dots" | "bars" | "custom"
      loadingSkeleton={<FormSkeleton />}
      loadingComponent={<CustomLoader />} // para loadingMode="custom"
      
      // Estilos
      className="my-custom-class"
      styles={{
        container: "...",
        content: "...",
        loading: "...",
        success: "...",
        error: "...",
        icon: "...",
        message: "...",
        resetButton: "...",
      }}
      
      // Tema local (sobrescreve o Provider)
      theme={{
        colors: { success: '#10b981' },
        icons: { success: <CustomIcon /> },
      }}
    >
      {/* Conteúdo do formulário */}
    </FormWithInlineReaction>
  );
}

ButtonWithInlineReaction

Botão com feedback visual inline.

import { ButtonWithInlineReaction, useInlineReaction } from '@techify/ui';

function SaveButton() {
  const { status, setLoading, setSuccess, setError, reset } = useInlineReaction();

  return (
    <ButtonWithInlineReaction
      status={status}
      onClick={handleSave}
      
      // Textos
      loadingText="Salvando..."
      successText="Salvo!"
      errorText="Erro ao salvar"
      
      // Variante e tamanho
      variant="primary" // "primary" | "secondary" | "outline" | "ghost" | "destructive" | "success" | "warning"
      size="md" // "xs" | "sm" | "md" | "lg" | "xl"
      
      // Comportamento
      type="button" // "button" | "submit" | "reset"
      disabled={false}
      fullWidth={false}
      
      // Auto-reset
      autoReset={true}
      autoResetDelay={2000} // ms
      onAutoReset={reset}
      
      // Estilos
      className="..."
      styles={{ button: "..." }}
    >
      Salvar
    </ButtonWithInlineReaction>
  );
}

InputWithInlineReaction

Input com validação visual em tempo real.

import { InputWithInlineReaction } from '@techify/ui';
import { useState } from 'react';
import type { ReactionStatus } from '@techify/ui';
import { Mail, Check } from 'lucide-react';

function EmailInput() {
  const [status, setStatus] = useState<ReactionStatus>('idle');

  const validateEmail = async (value: string) => {
    if (!value) return setStatus('idle');
    
    setStatus('loading');
    const isValid = await checkEmail(value);
    setStatus(isValid ? 'success' : 'error');
  };

  return (
    <InputWithInlineReaction
      type="email"
      status={status}
      onChange={validateEmail}
      
      // Labels e mensagens
      label="Email"
      placeholder="[email protected]"
      helperText="Digite seu melhor email"
      successMessage="Email válido!"
      errorMessage="Email inválido"
      
      // Tamanho e variante
      size="md" // "xs" | "sm" | "md" | "lg" | "xl"
      variant="default" // "default" | "filled" | "outline"
      
      // Ícones
      leftIcon={<Mail size={16} />}
      rightIcon={<Check size={16} />} // aparece quando status é idle
      
      // Estilos
      className="..."
      styles={{
        container: "...",
        input: "...",
        icon: "...",
        message: "...",
      }}
    />
  );
}

CardWithInlineReaction

Card com estados de feedback para operações assíncronas.

import { CardWithInlineReaction, useInlineReaction, Skeleton } from '@techify/ui';

function ProductCard({ product }) {
  const { status, setLoading, setSuccess, setError, reset } = useInlineReaction();

  return (
    <CardWithInlineReaction
      status={status}
      onReset={reset}
      
      // Mensagens
      successMessage="Adicionado ao carrinho!"
      errorMessage="Erro ao adicionar"
      resetButtonText="Voltar"
      
      // Loading
      loadingMode="blur" // "skeleton" | "spinner" | "blur" | "pulse"
      loadingSkeleton={<Skeleton className="h-48" />}
      
      // Tamanho e estilo
      size="md"
      padding="md" // Size ou number (px)
      shadow="md" // "none" | "sm" | "md" | "lg" | "xl"
      border={true}
      hoverable={true}
      
      // Estilos
      className="..."
      styles={{
        card: "...",
        content: "...",
        loading: "...",
        success: "...",
        error: "...",
      }}
    >
      <img src={product.image} alt={product.name} />
      <h3>{product.name}</h3>
      <button onClick={handleAddToCart}>Adicionar</button>
    </CardWithInlineReaction>
  );
}

FormBuilder (React Hook Form + Zod)

Gerador de formulários automático com validação em tempo real e inline reactions.

import { FormBuilder, useInlineReaction } from '@techify/ui';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import type { FormField } from '@techify/ui';

// Schema Zod
const schema = z.object({
  name: z.string().min(2, 'Nome muito curto'),
  email: z.string().email('Email inválido'),
  message: z.string().min(10, 'Mensagem muito curta'),
});

type FormData = z.infer<typeof schema>;

function ContactForm() {
  const { status, setLoading, setSuccess, setError, reset } = useInlineReaction();
  
  const form = useForm<FormData>({
    resolver: zodResolver(schema),
    mode: 'onChange', // Importante para validação em tempo real
  });

  const fields: FormField<FormData>[] = [
    {
      name: 'name',
      type: 'text',
      label: 'Nome',
      placeholder: 'Seu nome',
      required: true,
      successMessage: 'Nome válido ✓', // Mostrado quando válido
    },
    {
      name: 'email',
      type: 'email',
      label: 'Email',
      placeholder: '[email protected]',
      required: true,
      successMessage: 'Email válido ✓',
    },
    {
      name: 'message',
      type: 'textarea',
      label: 'Mensagem',
      placeholder: 'Sua mensagem...',
      required: true,
      rows: 4,
      colSpan: 'full',
      successMessage: 'Mensagem válida ✓',
    },
  ];

  const onSubmit = async (data: FormData) => {
    setLoading();
    try {
      await sendMessage(data);
      setSuccess();
      form.reset();
    } catch {
      setError();
    }
  };

  return (
    <FormBuilder
      form={form}
      fields={fields}
      onSubmit={onSubmit}
      status={status}
      onReset={reset}
      
      // Layout
      columns={2} // 1 | 2 | 3 | 4 | 6 | 12
      gap="md" // "xs" | "sm" | "md" | "lg" | "xl"
      
      // Textos
      submitText="Enviar"
      loadingText="Enviando..."
      resetText="Limpar"
      showResetButton={true}
      
      // Mensagens de feedback
      successMessage="Enviado com sucesso! 🎉"
      errorMessage="Erro ao enviar. Tente novamente."
      
      // 🆕 Validação em tempo real com inline reaction
      validationMode="inline-reaction" // "default" | "inline-reaction" | "both"
      validateDebounce={300} // ms
      
      // Customização de inputs
      inputBorderRadius="md" // "none" | "sm" | "md" | "lg" | "xl" | "full"
      inputBorderWidth="1px"
      
      // Estilos
      successColor="#10b981"
      successBackgroundColor="#ecfdf5"
    />
  );
}

Modos de Validação

| Modo | Descrição | |------|-----------| | 'default' | Mostra erros em texto abaixo do campo | | 'inline-reaction' | Mostra ícones de loading/success/error no campo | | 'both' | Mostra ambos (ícones + texto) |

Tipos de Campo Suportados

text, email, password, number, tel, url, textarea, select, checkbox, radio, switch, date, time, datetime-local, file, hidden, custom

InlineReaction (Standalone)

Componente de reação standalone para uso em qualquer contexto.

import { InlineReaction, useInlineReaction } from '@techify/ui';

function StatusDisplay() {
  const { status, setLoading, setSuccess, setError, reset } = useInlineReaction();

  return (
    <div>
      {status === 'idle' ? (
        <div>Conteúdo normal</div>
      ) : (
        <InlineReaction
          status={status}
          onReset={reset}
          
          // Mensagens
          successMessage="Operação concluída!"
          errorMessage="Algo deu errado"
          loadingMessage="Processando..."
          resetButtonText="Voltar"
          
          // Exibição
          showResetButton={true}
          size="md" // "xs" | "sm" | "md" | "lg" | "xl"
          
          // Estilos
          className="..."
          styles={{
            container: "...",
            icon: "...",
            message: "...",
            resetButton: "...",
            loading: "...",
            success: "...",
            error: "...",
          }}
        />
      )}
    </div>
  );
}

📱 Componentes Responsivos

ResponsiveModal

Componente inteligente que usa Modal no desktop e Drawer no mobile automaticamente.

import { ResponsiveModal, useResponsiveModal } from '@techify/ui';

function MyComponent() {
  const { open, setOpen, isMobile } = useResponsiveModal();

  return (
    <>
      <button onClick={() => setOpen(true)}>
        {isMobile ? 'Abrir Drawer' : 'Abrir Modal'}
      </button>
      
      <ResponsiveModal
        open={open}
        onOpenChange={setOpen}
        title="Modal Responsivo"
        description="Adapta-se automaticamente ao dispositivo"
        
        // Configuração do Drawer (mobile)
        drawerSide="bottom" // "top" | "bottom" | "left" | "right"
        showHandle={true}
        draggable={true}
        
        // Breakpoint customizado
        mobileBreakpoint={768}
        
        // Forçar modo específico
        forceMode={undefined} // "modal" | "drawer" | undefined (auto)
        
        footer={
          <>
            <button onClick={() => setOpen(false)}>Cancelar</button>
            <button onClick={handleConfirm}>Confirmar</button>
          </>
        }
      >
        <p>Conteúdo do modal/drawer</p>
      </ResponsiveModal>
    </>
  );
}

Modal

Modal para desktop com animações suaves.

import { Modal } from '@techify/ui';
import { useState } from 'react';

function DesktopModal() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen(true)}>Abrir Modal</button>
      
      <Modal
        open={open}
        onOpenChange={setOpen}
        title="Título do Modal"
        description="Descrição opcional"
        size="md" // "xs" | "sm" | "md" | "lg" | "xl" | "full"
        showCloseButton={true}
        closeOnOverlayClick={true}
        closeOnEscape={true}
        footer={<button onClick={() => setOpen(false)}>Fechar</button>}
      >
        <p>Conteúdo do modal</p>
      </Modal>
    </>
  );
}

Drawer

Drawer/Sheet com suporte a arraste para fechar.

import { Drawer } from '@techify/ui';
import { useState } from 'react';

function MobileDrawer() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen(true)}>Abrir Menu</button>
      
      <Drawer
        open={open}
        onOpenChange={setOpen}
        side="bottom" // "top" | "bottom" | "left" | "right"
        size="md" // "xs" | "sm" | "md" | "lg" | "xl" | "full"
        title="Menu"
        
        // Recursos mobile
        showHandle={true} // Mostra handle de arraste
        draggable={true} // Permite arrastar para fechar
        
        closeOnOverlayClick={true}
        closeOnEscape={true}
        
        footer={<button onClick={() => setOpen(false)}>Fechar</button>}
      >
        <nav>
          <a href="#">Link 1</a>
          <a href="#">Link 2</a>
          <a href="#">Link 3</a>
        </nav>
      </Drawer>
    </>
  );
}

useIsMobile Hook

Hook para detectar se o dispositivo é mobile.

import { useIsMobile, MOBILE_BREAKPOINT } from '@techify/ui';

function ResponsiveComponent() {
  const isMobile = useIsMobile(); // Padrão: 768px
  const isTablet = useIsMobile(1024); // Breakpoint customizado

  return (
    <div>
      {isMobile ? (
        <MobileLayout />
      ) : (
        <DesktopLayout />
      )}
    </div>
  );
}

🔄 Modos de Loading

A biblioteca oferece 6 modos de loading diferentes:

import { 
  LoadingSpinner,
  LoadingBlur, 
  LoadingPulse,
  LoadingDots,
  LoadingBars,
  LoadingRenderer 
} from '@techify/ui';

// Uso direto dos componentes
<LoadingSpinner size="md" text="Carregando..." showText />
<LoadingBlur intensity={8}>{children}</LoadingBlur>
<LoadingPulse>{children}</LoadingPulse>
<LoadingDots />
<LoadingBars />

// Renderizador automático baseado no modo
<LoadingRenderer
  mode="spinner" // "skeleton" | "spinner" | "blur" | "pulse" | "dots" | "bars" | "custom"
  skeleton={<MySkeleton />}
  customComponent={<MyCustomLoader />}
  spinnerSize="md"
  spinnerColor="#3b82f6"
  blurIntensity={8}
  text="Carregando..."
  showText
>
  {children}
</LoadingRenderer>

🎣 Hook useInlineReaction

import { useInlineReaction } from '@techify/ui';

function MyComponent() {
  const {
    // Estado
    status,        // 'idle' | 'loading' | 'success' | 'error'
    
    // Setters
    setStatus,     // (status: ReactionStatus) => void
    setLoading,    // () => void
    setSuccess,    // () => void
    setError,      // () => void
    reset,         // () => void
    resetStatus,   // Alias para reset
    
    // Helpers booleanos
    isIdle,        // status === 'idle'
    isLoading,     // status === 'loading'
    isSuccess,     // status === 'success'
    isError,       // status === 'error'
  } = useInlineReaction({
    // Opções
    initialStatus: 'idle',
    autoResetDelay: 3000, // Auto-reset após sucesso/erro (ms)
    onStatusChange: (status) => console.log('Status:', status),
  });
}

🎨 Personalização

Provider Global (estilo shadcn)

Configure uma vez, use em todos os componentes:

import { TechifyUIProvider } from '@techify/ui';

function App() {
  return (
    <TechifyUIProvider
      config={{
        // Cores
        colors: {
          success: '#22c55e',
          error: '#ef4444',
          loading: '#3b82f6',
          warning: '#f59e0b',
          primary: '#6366f1',
          primaryForeground: '#ffffff',
          secondary: '#f1f5f9',
          secondaryForeground: '#0f172a',
          destructive: '#ef4444',
          background: '#ffffff',
          foreground: '#0f172a',
          muted: '#f1f5f9',
          mutedForeground: '#64748b',
          border: '#e2e8f0',
        },
        
        // Ícones customizados
        icons: {
          success: <CheckCircle className="w-12 h-12" />,
          error: <XCircle className="w-12 h-12" />,
          loading: <Loader2 className="w-12 h-12 animate-spin" />,
          warning: <AlertTriangle className="w-12 h-12" />,
        },
        
        // Animações
        animation: {
          fadeVariants: {
            hidden: { opacity: 0, filter: 'blur(8px)' },
            visible: { opacity: 1, filter: 'blur(0px)' },
            exit: { opacity: 0, filter: 'blur(8px)' },
          },
          defaultTransition: { duration: 0.25, ease: 'easeOut' },
          feedbackTransition: { duration: 0.3, ease: 'easeOut' },
          enabled: true,
        },
        
        // Loading
        loading: {
          mode: 'spinner', // Modo padrão
          text: 'Carregando...',
          showText: false,
          spinnerSize: 'md',
          blurIntensity: 8,
        },
        
        // Feedback
        feedback: {
          showIcon: true,
          showMessage: true,
          showResetButton: true,
          resetButtonText: 'Voltar',
          autoReset: false,
          autoResetDelay: 3000,
        },
        
        // Valores padrão
        defaults: {
          size: 'md',
          variant: 'primary',
          loadingMode: 'skeleton',
        },
        
        // 🆕 Configuração de inputs
        input: {
          borderRadius: '0.5rem',
          borderWidth: '1px',
          backgroundColor: 'transparent',
          focusRingWidth: '2px',
          focusRingOffset: '2px',
          focusRingColor: 'var(--tui-ring)',
        },
        
        // 🆕 Configuração de botões
        button: {
          borderRadius: '0.5rem',
        },
        
        // 🆕 Fonte global
        fontFamily: 'inherit', // Herda do projeto
        
        // Estilos globais
        styles: {
          container: '',
          content: '',
          loading: '',
          success: '',
          error: '',
          icon: '',
          message: '',
          resetButton: '',
          button: '',
          input: '',
          card: '',
          form: '',
        },
      }}
    >
      <YourApp />
    </TechifyUIProvider>
  );
}

CSS Variables

/* Variáveis disponíveis dentro de [data-tui] */
[data-tui] {
  /* Cores principais */
  --tui-success: #22c55e;
  --tui-error: #ef4444;
  --tui-loading: #3b82f6;
  --tui-warning: #f59e0b;
  
  /* Cores de UI */
  --tui-background: #ffffff;
  --tui-foreground: #0f172a;
  --tui-muted: #f1f5f9;
  --tui-muted-foreground: #64748b;
  --tui-border: #e2e8f0;
  --tui-ring: #3b82f6;
  
  /* Cores de componentes */
  --tui-primary: #3b82f6;
  --tui-primary-foreground: #ffffff;
  --tui-secondary: #f1f5f9;
  --tui-secondary-foreground: #0f172a;
  --tui-destructive: #ef4444;
  --tui-destructive-foreground: #ffffff;
  
  /* 🆕 Bordas e raios */
  --tui-input-radius: 0.375rem;
  --tui-button-radius: 0.375rem;
  --tui-border-width: 1px;
  
  /* 🆕 Focus ring */
  --tui-focus-ring-width: 2px;
  --tui-focus-ring-offset: 2px;
  --tui-focus-ring-color: var(--tui-ring);
  
  /* 🆕 Fonte */
  --tui-font-family: inherit;
}

/* Dark Mode */
.dark {
  --tui-success: #4ade80;
  --tui-error: #f87171;
  --tui-loading: #60a5fa;
  --tui-warning: #fbbf24;
  --tui-background: #0f172a;
  --tui-foreground: #f1f5f9;
  --tui-muted: #1e293b;
  --tui-muted-foreground: #94a3b8;
  --tui-border: #334155;
  --tui-primary: #60a5fa;
  --tui-secondary: #334155;
}

Tema por Componente

Sobrescreva configurações localmente:

<ButtonWithInlineReaction
  status={status}
  onClick={onClick}
  theme={{
    colors: { success: '#10b981' },
    icons: { success: <Heart /> },
    defaults: { size: 'lg' },
  }}
>
  Curtir
</ButtonWithInlineReaction>

🦴 Skeletons Prontos

import {
  // Primitivos
  Skeleton,
  SkeletonInput,
  SkeletonLabel,
  SkeletonTextarea,
  SkeletonButton,
  SkeletonSwitch,
  SkeletonFormField,
  
  // Formulários completos
  FormSkeleton,
  ContactFormSkeleton,
  BlogFormSkeleton,
  ShowcaseFormSkeleton,
  LearningFormSkeleton,
  DevLogFormSkeleton,
  CertificationFormSkeleton,
} from '@techify/ui';

// Uso
<FormWithInlineReaction
  status={status}
  loadingSkeleton={<ContactFormSkeleton />}
>
  {/* ... */}
</FormWithInlineReaction>

📋 API Reference

Tipos

import type {
  // Estados e modos
  ReactionStatus,        // 'idle' | 'loading' | 'success' | 'error'
  LoadingMode,           // 'skeleton' | 'spinner' | 'blur' | 'pulse' | 'dots' | 'bars' | 'custom'
  Size,                  // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
  Variant,               // 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'success' | 'warning'
  
  // Configuração
  TechifyUIConfig,
  ColorConfig,
  IconConfig,
  AnimationOptions,
  LoadingConfig,
  FeedbackConfig,
  ComponentStyles,
  DefaultsConfig,
  InputConfig,           // 🆕 Configuração de inputs
  ButtonConfig,          // 🆕 Configuração de botões
  
  // Props de componentes
  FormWithInlineReactionProps,
  ButtonWithInlineReactionProps,
  InlineReactionProps,
  InputWithInlineReactionProps,
  CardWithInlineReactionProps,
  TechifyUIProviderProps,
  
  // 🆕 FormBuilder
  FormBuilderProps,      // Props do FormBuilder
  FormField,             // Configuração de campo
  FieldType,             // Tipos de campo suportados
  FieldOption,           // Opção para select/radio
  
  // Componentes Responsivos
  ModalProps,            // Props do Modal
  DrawerProps,           // Props do Drawer
  DrawerSide,            // 'top' | 'bottom' | 'left' | 'right'
  ResponsiveModalProps,  // Props do ResponsiveModal
  
  // Hook
  UseInlineReactionReturn,
  UseInlineReactionOptions,
  
  // Animações (Framer Motion)
  AnimationVariants,
  AnimationConfig,
} from '@techify/ui';

Utilitários

import {
  // Classes
  cn,                   // Combina classes (clsx + tailwind-merge)
  
  // Animações
  fadeVariants,         // Variantes fade + blur
  scaleVariants,        // Variantes scale
  slideVariants,        // Variantes slide
  defaultTransition,    // Transição padrão
  feedbackTransition,   // Transição para feedback
  
  // Context
  TechifyUIProvider,    // Provider
  TechifyUIContext,     // Context (para uso avançado)
  useTechifyUI,         // Hook para acessar config global
  useConfig,            // Hook para merge config local + global
  defaultConfig,        // Configuração padrão
  
  // Hooks responsivos
  useIsMobile,          // Hook para detectar mobile
  useResponsiveModal,   // Hook auxiliar para ResponsiveModal
  MOBILE_BREAKPOINT,    // Breakpoint padrão (768px)
  
  // 🆕 FormBuilder
  FormBuilder,          // Gerador de formulários
} from '@techify/ui';

Dependências Opcionais (para FormBuilder)

Para usar o FormBuilder com validação Zod, instale:

pnpm add react-hook-form zod @hookform/resolvers

🔗 Peer Dependencies

| Pacote | Versão | |--------|--------| | react | >= 18.0.0 | | react-dom | >= 18.0.0 | | framer-motion | >= 10.0.0 |


📄 Licença

MIT © Techify


Feito com ❤️ pela equipe Techify

GitHubnpm