Tucano v0.37.2

Máscaras

Da formato mientras se escribe, comprueba el dígito verificador de documentos y oculta datos sensibles. No sustituye el campo por otro control: es comportamiento sobre tu <input>, que sigue con su name y con el estilo que el proyecto ya aplica.

El CPF es el documento fiscal de las personas en Brasil, y el importe está en reales brasileños. Escribe un CPF no válido y sal del campo: el navegador pasa a bloquear el envío.

Ejemplos

Todo es el mismo data-tuc-mask con otro formato. El texto de espera sale del patrón — nadie escribe 000.000.000-00 a mano. Los formatos con nombre siguen documentos y convenciones de Brasil; el patrón libre sirve para cualquier otra cosa.

CNPJ

El documento fiscal de las empresas en Brasil. Acepta el formato nuevo, con letras.

CPF o CNPJ

Cambia de patrón según la longitud.

Teléfono

Fijo brasileño con 8 dígitos o móvil con 9.

Móvil

Siempre con 9 dígitos.

CEP

cep, el código postal brasileño.

Tarjeta

card, en grupos de cuatro.

Fecha y hora, en texto

date y time — solo la máscara, sin calendario.

Moneda sin símbolo

currency, y tres decimales con data-decimals="3".

Otra moneda

data-currency="USD" sobre el currency.

Patrón libre

data-tuc-mask="##/##", caducidad de la tarjeta.

Patrón con letras

AAA-####: A es letra, # es dígito. El patrón libre no pasa a mayúsculas.

Con error del servidor

aria-invalid="true" en el campo.

CNPJ no válido.

Cómo usar

Marca el <input> con el formato, y se inicializa solo al cargar y en cada htmx:afterSwap. El componente pone la clase .tuc-input en el campo y un inputmode numérico cuando el patrón no acepta letras, para que el móvil abra el teclado de números.

<input name="cpf"      data-tuc-mask="cpf" data-validate="true">
<input name="document" data-tuc-mask="cpf-cnpj">
<input name="phone"    data-tuc-mask="phone">
<input name="amount"   data-tuc-mask="brl">
<input name="expiry"   data-tuc-mask="##/##">
FormatoResultado
cpf111.444.777-35
cnpj11.222.333/0001-81 — acepta letras en las 12 primeras posiciones
cnpj-numericLo mismo, rechazando letras
cpf-cnpjUno u otro, según la longitud
phone(69) 3344-5566 o (69) 99988-7766
mobile(69) 99988-7766
cep69900-000
date time25/12/2026 · 14:30
card4111 1111 1111 1111
realR$ 1.234,50
currency1.234,50, sin símbolo — con data-currency, el de la moneda
patrón libre# dígito, A letra, * ambos; el resto entra tal cual
AtributoPor defectoPara qué
data-tuc-mask—Nombre del formato o patrón libre
data-validatefalseComprueba el dígito verificador al salir del campo
data-error-textdel formatoMensaje del navegador cuando la validación falla
data-decimals2Decimales en la moneda
data-currency—Código de la moneda, como BRL o USD
data-tuc-reveal—Ojo para mostrar y ocultar; el valor elige el modo
data-reveal-visible2Caracteres visibles en el modo end
data-reveal-modesegún el campoend, email o all

En el formulario de Django

class CustomerForm(forms.ModelForm):
    class Meta:
        model = Customer
        fields = ["document", "phone", "credit_limit"]
        widgets = {
            "document": forms.TextInput(attrs={"data-tuc-mask": "cpf-cnpj", "data-validate": "true"}),
            "phone": forms.TextInput(attrs={"data-tuc-mask": "phone"}),
            "credit_limit": forms.TextInput(attrs={"data-tuc-mask": "brl"}),
        }

El campo envía el texto tal como está en pantalla: 111.444.777-35, R$ 1.234,50. Límpialo en el clean_ — la validación del navegador protege la pantalla, no el servidor.

def clean_document(self):
    return re.sub(r"[^0-9A-Z]", "", self.cleaned_data["document"].upper())

def clean_credit_limit(self):
    text = re.sub(r"[^\d,]", "", self.cleaned_data["credit_limit"])
    return Decimal(text.replace(",", ".")) if text else None

En JavaScript

const m = new Tucano.Mask('#amount', { format: 'brl' });
m.getRaw();          // '123450' — sin puntuación
m.getNumber();       // 1234.5 — solo en los formatos de moneda; en los demás, null
m.setValue('99');    // da formato y dispara tucano:change
m.isValid();         // comprueba el dígito verificador, en los formatos que lo tienen
m.destroy();

document.querySelector('#amount').addEventListener('tucano:change', (e) => {
  e.detail.value;    // 'R$ 1.234,50'
  e.detail.raw;      // '123450'
  e.detail.number;   // 1234.5
});

Las utilidades detrás de la máscara también vienen listas, para validar o dar formato fuera de un campo:

Tucano.mask.validateCPF('111.444.777-35')              // true
Tucano.mask.validateCNPJ('11.222.333/0001-81')         // true
Tucano.mask.validateCpfCnpj('123.456.789-01')          // false
Tucano.mask.format('12345678901', 'cpf')               // '123.456.789-01'
Tucano.mask.applyCurrency('12345', { currency: 'BRL' }) // 'R$ 123,45'
Tucano.mask.maskEmail('contato@empresa.com.br')        // 'c••••••@empresa.com.br'

Validación

Con data-validate, el dígito verificador se comprueba al salir del campo. Si falla, el campo recibe aria-invalid="true" y setCustomValidity, y el propio navegador bloquea el envío — sin una línea en el proyecto.

Y el acierto se ve al momento: en cuanto el valor está completo y correcto, el campo se pone verde, sin esperar a salir de él. El error espera — mientras se escribe, un CPF a medias nunca se pone rojo; solo se señala al salir del campo, y vuelve a desaparecer cuando la persona lo corrige. El verde es el atributo data-tuc-valid, pintado con --tuc-success.

Valen cpf, cnpj, cnpj-numeric y cpf-cnpj; los demás formatos no tienen dígito que comprobar. Un campo vacío pasa: la obligatoriedad es cosa de required. Mientras se escribe el error desaparece, y al volver al campo también — incluida la marca que vino del servidor, porque la persona está corrigiendo.

<input name="cpf" data-tuc-mask="cpf" data-validate="true" data-error-text="Revisa el CPF">

CNPJ alfanumérico

El formato nuevo mantiene las 14 posiciones y la misma máscara: las 12 primeras aceptan letras, las 2 últimas siguen siendo numéricas, y el dígito verificador usa el código ASCII menos 48 — lo que deja el cálculo antiguo válido para CNPJ solo de números. cnpj acepta los dos y pasa las letras a mayúsculas; cnpj-numeric rechaza letras, para quien todavía no puede recibirlas. Confirma la fecha de entrada en vigor en la Nota Técnica de la Receita Federal de Brasil antes de exigirlo en producción.

Campo sensible

data-tuc-reveal pone un ojo para mostrar y ocultar. Un campo que ya llega relleno nace oculto, con solo el final visible; un campo vacío nace visible, porque quien escribe necesita ver. La contraseña es la excepción: nace siempre oculta.

CPF de un registro

Modo end, con dos visibles.

Tarjeta

data-reveal-visible="4" deja los cuatro últimos.

Correo

En type="email", deja el dominio.

Clave de API

data-tuc-reveal="all" no muestra nada.

Contraseña

En type="password" el ojo solo cambia el type.

Vacío

Nace visible; el ojo lo oculta después de escribir.

ModoCuándoResultado
end, el predeterminadoDocumento, tarjeta, cuenta, teléfono•••• •••• •••• 1234
emailAutomático en type="email"j•••••••••@empresa.com.br
allToken, clave de API••••••••••••••••••••
<input name="cpf" value="111.444.777-35" data-tuc-mask="cpf" data-tuc-reveal>
<input name="card" data-tuc-mask="card" data-tuc-reveal data-reveal-visible="4">
<input name="api_key" data-tuc-reveal="all">
<input type="email" name="email" data-tuc-reveal>
<input type="password" name="password" data-tuc-reveal>

Lo que el formulario envía no cambia por estar oculto. Fuera de type="password", el name pasa a un <input type="hidden"> con el valor real, y el campo visible muestra los puntos. Mientras está oculto, el campo es de solo lectura: escribir sobre los puntos sobrescribiría el valor sin que la persona se diera cuenta. Los separadores siguen en su sitio, para que la forma siga siendo reconocible: •••.•••.•••-35.

Con máscara, el oculto lleva el valor sin puntuación

En un campo con data-tuc-mask y data-tuc-reveal, lo que llega al servidor es 11144477735, y no 111.444.777-35 — es el getRaw(). Sin máscara, va el texto tal como se escribió.

Por qué nacer oculto, y por qué el correo es al revés

Capturas de pantalla, grabaciones de soporte, alguien mirando por encima del hombro: el dato completo aparecía sin que nadie lo pidiera. Nacer oculto es la diferencia entre exponer por defecto y exponer por elección. En el correo el dominio es lo que ayuda a reconocer la cuenta y la parte local es lo que identifica a la persona — dejar el final revelaría om.br y ocultaría lo útil. Sin @, lo oculta todo.

Texto suelto en la pantalla

No todo dato sensible está en un campo: el CPF en la pantalla de un cliente, la tarjeta en una celda de tabla, la clave de API en un párrafo. data-tuc-reveal vale en cualquier elemento, en los mismos modos, y junto con data-tuc-format. El texto nace oculto, con el ojo al lado.

ClienteCPFTarjetaCorreo
Juan García 11144477735 4111 1111 1111 1234 juan.garcia@empresa.com

Clave de API: sk_live_a1b2c3d4e5f6

<td><span data-tuc-format="cpf" data-tuc-reveal>{{ customer.cpf }}</span></td>
<td><span data-tuc-reveal data-reveal-visible="4">{{ card.number }}</span></td>
<p>Clave de API: <span data-tuc-reveal="all">{{ api_key }}</span></p>

En la pantalla, ocultar es solo visual

El valor completo sigue en el HTML: quien abre el código de la página lo ve. Sirve contra capturas, grabaciones y quien mira por encima del hombro. Si el dato no puede llegar al navegador, ocúltalo en el servidor y envía solo lo que puede aparecer, como los cuatro últimos dígitos de la tarjeta.

Solo para mostrar

Para lo que ya viene de la base de datos y no se edita, no hace falta un campo: data-tuc-format da formato al texto del propio elemento.

MarcadoEn pantalla
data-tuc-format="cpf" · 1234567890112345678901
data-tuc-format="cnpj" · 1122233300018111222333000181
data-tuc-format="cnpj" · 12ABC34501DE3512ABC34501DE35
data-tuc-format="phone" · 6999988776669999887766
data-tuc-format="cep" · 6990000069900000
data-tuc-format="brl" · 1234.51234.5
data-tuc-format="currency" data-decimals="3" · 12.512.5
<span data-tuc-format="cpf">{{ customer.cpf }}</span>
<span data-tuc-format="brl">{{ order.total }}</span>
<span data-tuc-format="phone" data-value="{{ customer.phone }}"></span>

Acepta cpf, cnpj, cpf-cnpj, phone, mobile, cep, card, real, currency y patrón libre, con data-decimals y data-currency en la moneda. El valor sale del texto del elemento o de data-value. La moneda lee el punto decimal de la base de datos (1234.5) y también la coma. Un contenido que no completa el patrón vuelve tal como vino, en lugar de salir a medias y parecer corrupto. Para HTML insertado por otro camino que no sea HTMX, Tucano.autoFormat(element).

Teclado y accesibilidad

La máscara usa el teclado del navegador, sin atajos propios. De lo que se ocupa es de que el cursor no se pierda.

TeclaAcción
Backspace DeleteSobre un separador, borra el carácter vecino, en lugar de trabarse
PegarDescarta lo que no cabe: abc111x444.777--35 se convierte en 111.444.777-35
Escribir en la monedaSe llena de derecha a izquierda, con el cursor siempre al final
Tab hasta el ojoEs un <button>: Enter y Espacio alternan

El ojo anuncia "Mostrar" u "Ocultar" y el estado con aria-pressed. Al mostrar, el foco va al campo. Si falla la validación, el campo queda con aria-invalid="true", que el lector de pantalla lee como no válido.

API

Generada a partir del código en cada build — si algo no está aquí, no existe.

Marcado
[data-tuc-mask], [data-tuc-reveal] [data-tuc-format]:not([data-tuc-formatted])
En JS
new Tucano.Mask(alvo, opcoes) Tucano.autoFormat()
Atributos
data-currency data-decimals data-error-text data-reveal-mode data-reveal-visible data-tuc-format data-tuc-mask data-tuc-reveal data-validate data-value
Métodos
getRaw getNumber setValue isValid destroy
Eventos
tucano:change

Opciones

Las notas de esta tabla salen de los comentarios del código fuente, que están escritos en portugués.

OpciónPor defectoPara qué
formatnullnome de FORMATS ou gabarito livre
validatefalsevalida no blur e bloqueia o submit
decimals2
currencynull'BRL' formata com R$
revealfalseolhinho para mostrar e ocultar
revealVisible2quantos caracteres ficam a mostra no modo 'end'
revealModenull'end' | 'email' | 'all'. null decide pelo campo
localeundefined
errorTextnull
onChangenull