Máscaras
Formata enquanto se digita, confere o dígito verificador de documento e esconde dado sensível.
Não troca o campo por outro controle: é comportamento sobre o seu <input>, que continua com o
name e com o estilo que o projeto já aplica.
Digite um CPF inválido e saia do campo: o navegador passa a barrar o envio.
Exemplos
Tudo é o mesmo data-tuc-mask com outro formato. O texto de espera sai do gabarito — ninguém escreve 000.000.000-00 à mão.
CNPJ
Aceita o formato novo, com letras.
CPF ou CNPJ
Troca de gabarito pelo tamanho.
Telefone
Fixo com 8 dígitos ou celular com 9.
Celular
Sempre com 9 dígitos.
CEP
cep
Cartão
card, em grupos de quatro.
Data e hora, em texto
date e time — só a máscara, sem calendário.
Moeda sem símbolo
currency, e três casas com data-decimals="3".
Outra moeda
data-currency="USD" sobre o currency.
Gabarito livre
data-tuc-mask="##/##", validade do cartão.
Gabarito com letras
AAA-####: A é letra, # é dígito. Gabarito livre não passa para maiúscula.
Com erro do servidor
aria-invalid="true" no campo.
CNPJ inválido.
Como usar
Marque o <input> com o formato, e ele inicializa sozinho no carregamento e a cada
htmx:afterSwap. O componente veste a classe .tuc-input no campo e põe o inputmode numérico
quando o gabarito não aceita letra, para o celular abrir o 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="##/##">| Formato | Resultado |
|---|---|
cpf | 111.444.777-35 |
cnpj | 11.222.333/0001-81 — aceita letras nas 12 primeiras posições |
cnpj-numeric | O mesmo, recusando letras |
cpf-cnpj | Um ou outro, pelo tamanho |
phone | (69) 3344-5566 ou (69) 99988-7766 |
mobile | (69) 99988-7766 |
cep | 69900-000 |
date time | 25/12/2026 · 14:30 |
card | 4111 1111 1111 1111 |
real | R$ 1.234,50 |
currency | 1.234,50, sem símbolo — com data-currency, o da moeda |
| gabarito livre | # dígito, A letra, * os dois; o resto entra como está |
| Atributo | Padrão | Para quê |
|---|---|---|
data-tuc-mask | — | Nome do formato ou gabarito livre |
data-validate | false | Confere o dígito verificador ao sair do campo |
data-error-text | do formato | Mensagem do navegador quando a validação reprova |
data-decimals | 2 | Casas decimais na moeda |
data-currency | — | Código da moeda, como BRL ou USD |
data-tuc-reveal | — | Olho de mostrar e ocultar; o valor escolhe o modo |
data-reveal-visible | 2 | Caracteres à mostra no modo end |
data-reveal-mode | pelo campo | end, email ou all |
No formulário do 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"}),
}O campo posta o texto como está na tela: 111.444.777-35, R$ 1.234,50. Limpe no clean_ —
a validação do navegador protege a tela, não o 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 NoneEm JavaScript
const m = new Tucano.Mask('#amount', { format: 'brl' });
m.getRaw(); // '123450' — sem pontuação
m.getNumber(); // 1234.5 — só nos formatos de moeda; nos outros, null
m.setValue('99'); // formata e dispara tucano:change
m.isValid(); // confere o dígito verificador, nos formatos que têm
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
});Os utilitários por trás da máscara também saem prontos, para validar ou formatar fora de um 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'Validação
Com data-validate, o dígito verificador é conferido ao sair do campo. Reprovado, o campo recebe
aria-invalid="true" e setCustomValidity, e o próprio navegador barra o envio — sem uma linha no projeto.
E o acerto aparece na hora: assim que o valor fica completo e certo, o campo fica verde, sem esperar sair dele. O erro
espera — digitando, um CPF pela metade nunca fica vermelho; ele só é acusado ao sair do campo, e some de novo quando a
pessoa volta a corrigir. O verde é o atributo data-tuc-valid, que o CSS pinta com --tuc-success.
Valem cpf, cnpj, cnpj-numeric e cpf-cnpj; os outros formatos não têm dígito
para conferir. Campo vazio passa: obrigatoriedade é do required. Enquanto se digita o erro some, e ao voltar ao campo
também — inclusive a marca que veio do servidor, porque a pessoa está corrigindo.
<input name="cpf" data-tuc-mask="cpf" data-validate="true" data-error-text="Confira o CPF">CNPJ alfanumérico
O formato novo mantém as 14 posições e a mesma máscara: as 12 primeiras aceitam letras, as 2 últimas seguem numéricas, e o dígito verificador usa o código ASCII menos 48 — o que deixa o cálculo antigo valendo para CNPJ só de números. O cnpj aceita os dois e passa as letras para maiúscula; cnpj-numeric recusa letras, para quem ainda não pode recebê-las. Confirme a data de vigência na Nota Técnica da Receita antes de exigir em produção.
Campo sensível
O data-tuc-reveal põe um olho para mostrar e ocultar. Campo que já chega preenchido nasce oculto,
com só o fim à mostra; campo vazio nasce visível, porque quem digita precisa ver. A senha é a exceção: nasce sempre oculta.
CPF de um cadastro
Modo end, com dois à mostra.
Cartão
data-reveal-visible="4" guarda os quatro últimos.
Em type="email", guarda o domínio.
Chave de API
data-tuc-reveal="all" não mostra nada.
Senha
Em type="password" o olho só troca o type.
Vazio
Nasce visível; o olho esconde depois de digitar.
| Modo | Quando | Resultado |
|---|---|---|
end, o padrão | Documento, cartão, conta, telefone | •••• •••• •••• 1234 |
email | Automático em type="email" | j•••••••••@empresa.com.br |
all | Token, chave 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>O que o formulário posta não muda por estar oculto. Fora do type="password", o name passa para um
<input type="hidden"> com o valor real, e o campo visível mostra os pontos. Enquanto oculto, o campo é só
leitura: digitar em cima dos pontos escreveria por cima do valor sem a pessoa perceber. Os separadores continuam no lugar,
para a forma seguir reconhecível: •••.•••.•••-35.
Com máscara, o escondido leva o valor sem pontuação
Num campo com data-tuc-mask e data-tuc-reveal, o que chega no servidor é 11144477735, e não 111.444.777-35 — é o getRaw(). Sem máscara, vai o texto como foi digitado.
Por que nascer oculto, e por que o e-mail é ao contrário
Print de tela, gravação de suporte, alguém olhando por cima do ombro: o dado completo aparecia sem ninguém pedir. Nascer oculto é a diferença entre expor por padrão e expor por escolha. No e-mail o domínio é o que ajuda a reconhecer a conta e a parte local é o que identifica a pessoa — guardar o fim revelaria om.br e esconderia o útil. Sem @, ele esconde tudo.
Texto solto na tela
Nem todo dado sensível está num campo: o CPF na tela de um cliente, o cartão numa célula de tabela, a chave de API num
parágrafo. O data-tuc-reveal vale em qualquer elemento, nos mesmos modos, e junto com
data-tuc-format. O texto nasce escondido, com o olho ao lado.
| Cliente | CPF | Cartão | |
|---|---|---|---|
| João Silva | 11144477735 | 4111 1111 1111 1234 | joao.silva@empresa.com.br |
Chave 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>Chave de API: <span data-tuc-reveal="all">{{ api_key }}</span></p>Na tela, esconder é só visual
O valor inteiro continua no HTML: quem abre o código da página vê. Serve contra print, gravação e quem olha por cima do ombro. Se o dado não pode chegar ao navegador, esconda no servidor e mande só o que pode aparecer, como os quatro últimos dígitos do cartão.
Só para exibir
Para o que já vem do banco e não se edita, não precisa de campo: data-tuc-format formata o texto do
próprio elemento.
| Marcação | Na tela |
|---|---|
data-tuc-format="cpf" · 12345678901 | 12345678901 |
data-tuc-format="cnpj" · 11222333000181 | 11222333000181 |
data-tuc-format="cnpj" · 12ABC34501DE35 | 12ABC34501DE35 |
data-tuc-format="phone" · 69999887766 | 69999887766 |
data-tuc-format="cep" · 69900000 | 69900000 |
data-tuc-format="brl" · 1234.5 | 1234.5 |
data-tuc-format="currency" data-decimals="3" · 12.5 | 12.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>Aceita cpf, cnpj, cpf-cnpj, phone, mobile, cep,
card, real, currency e gabarito livre, com data-decimals e data-currency
na moeda. O valor sai do texto do elemento ou de data-value. Moeda lê o ponto decimal do banco
(1234.5) e também a vírgula. Conteúdo que não preenche o gabarito volta como veio, em vez de sair pela metade
e parecer corrompido. Para HTML inserido por outro caminho que não o HTMX, Tucano.autoFormat(element).
Teclado e acessibilidade
A máscara usa o teclado do navegador, sem atalho próprio. O que ela cuida é de o cursor não se perder.
| Tecla | Ação |
|---|---|
Backspace Delete | Em cima de um separador, apaga o caractere vizinho, em vez de travar |
| Colar | Descarta o que não cabe: abc111x444.777--35 vira 111.444.777-35 |
| Digitar na moeda | Enche da direita para a esquerda, com o cursor sempre no fim |
Tab até o olho | É um <button>: Enter e Espaço alternam |
O olho anuncia "Mostrar" ou "Ocultar" e o estado por aria-pressed. Ao mostrar, o foco vai para o campo. Reprovado na
validação, o campo fica com aria-invalid="true", que o leitor de tela lê como inválido.
API
Gerada do código a cada build — se algo não está aqui, não existe.
[data-tuc-mask], [data-tuc-reveal] [data-tuc-format]:not([data-tuc-formatted])new Tucano.Mask(alvo, opcoes) Tucano.autoFormat()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-valuegetRaw getNumber setValue isValid destroytucano:changeOpções
| Opção | Padrão | Para quê |
|---|---|---|
format | null | nome de FORMATS ou gabarito livre |
validate | false | valida no blur e bloqueia o submit |
decimals | 2 | |
currency | null | 'BRL' formata com R$ |
reveal | false | olhinho para mostrar e ocultar |
revealVisible | 2 | quantos caracteres ficam a mostra no modo 'end' |
revealMode | null | 'end' | 'email' | 'all'. null decide pelo campo |
locale | undefined | |
errorText | null | |
onChange | null |