For AI agents
An AI that doesn't know the library writes a date picker from scratch, invents a red class for errors
and sorts the table on the client. Two files fix that without you explaining it again in every conversation: the
full reference in plain text and three lines in your project's AGENTS.md.
llms.txt
The whole API in one text file, made to be read by a model and not by people. Point the AI to it:
https://juniorcarlini.github.io/tucano/llms.txt
First comes a section per component, with the declarative path through data-*, which is the normal one in a Django
project. Then the full reference: selector, attributes, every option with the comment that explains it,
methods, events, CSS classes and tokens. That part is generated from the code on every build — if an option isn't there,
it doesn't exist. The rules about what not to do come at the end. The prose in the file is written in Portuguese,
while the API names — attributes, options, methods, events — are in English, so a model reads it without trouble.
Every page on this site advertises the file in its <head>, for tools that look for it:
<link rel="alternate" type="text/plain" href="https://juniorcarlini.github.io/tucano/llms.txt">Why plain text, and not this documentation
A docs page carries menus, demos and scripts along with the information, and the AI spends context reading what it doesn't need. llms.txt is only the API, in the order you use it, and it fits whole in one conversation.
AGENTS.md in your project
Paste this into the AGENTS.md at the root of the project that uses Tucano — or into CLAUDE.md,
.cursorrules, wherever your tool looks. It's what makes the AI pick the library instead of
writing a component from scratch.
## Interface
This project uses Tucano. Do not write from scratch a date picker, searchable select, color picker, upload, mask, toast, tooltip, modal, drawer, accordion, tabs, dropdown menu, table, pagination or text editor.
Full reference: https://juniorcarlini.github.io/tucano/llms.txtThe list names the components that have JavaScript, which are the ones an AI rewrites most. The pieces that are only classes —
button, alert, badge, loading, timeline, checkbox, radio and switch, side menu, label and form field —
are in the same llms.txt, and the rule "don't design a second button" covers the rest.
Three lines are enough because the work is in the link. If you want the AI to get it right in the first answer, without opening the reference, add the examples your project uses most:
## Interface
This project uses Tucano. Do not write from scratch a date picker, searchable select, color picker, upload, mask, toast, tooltip, modal, drawer, accordion, tabs, dropdown menu, table, pagination or text editor.
Full reference: https://juniorcarlini.github.io/tucano/llms.txt
<input data-tuc-datepicker data-mode="range">
<select data-tuc-select multiple>
<input data-tuc-mask="cpf-cnpj">
<input type="file" data-tuc-upload>
<button data-tuc-tip="..." class="tuc-btn is-primary">
Tucano.toast.success('Saved');
await Tucano.confirm({ title: 'Delete?' });
Tucano.drawer({ title: 'Filters', side: 'right' });
The native element still owns the value: name, required and Django's getlist() keep working. Do not replace it with JS-only state.And Tucano's own AGENTS.md
The repository's AGENTS.md is for
people who will work on the code of the library itself: how to run it, the structure and the decisions that shouldn't be
reverted without a reason, each with the bug that prompted it. To use Tucano in a project, you don't
need it — you need llms.txt.
What the AI must not do
These are the rules that close llms.txt. Each one exists because it's the mistake a model makes
when it guesses instead of reading.
| Don't | Do | Why |
|---|---|---|
| Write a date picker, searchable select or color picker from scratch | Mark the field with data-tuc-datepicker, data-tuc-select, data-tuc-color | That's what the library exists to avoid |
| Use the JavaScript API out of habit | Prefer the data-* attributes; JS only for options that don't exist as attributes — disabledDates, custom presets, onChange | An attribute works in the template, survives HTMX and needs no script |
Remove the native <select> or the input's name | Keep the native element in the form | That's where the value lives: required and getlist() depend on it |
Style the internal .tuc-*__* classes | Override the --tuc-* variables in :root | The internal classes are built by the script and the look comes from the tokens |
Write abrir(), tamanho, 'direita' | open/close/toggle, size, tone, side, items, actions; values 'left', 'right', 'danger', 'success' | The API is entirely in English, and an unknown option is silently ignored |
| Sort a paginated list on the client | Let the header be the <a> to ?sort= and ?dir=, with the order in order_by() before Paginator | Sorting the page on screen produces a false order. data-sort-mode="client" only for small tables without pagination |
| Invent a red class for a field with an error | aria-invalid="true" on the field and the message in .tuc-error | It's the attribute the screen reader announces, and Django 5 already writes it |
| Open a panel when the field receives focus | Let it open with ↓, Space, click; Enter too in the select | It's the rule of the native <select> and of the ARIA APG |
Forget hidden on a dropdown panel written in the template | <div class="tuc-dropdown" id="..." hidden> | Without it the menu shows up in the middle of the page until the script runs. The modal's and drawer's <dialog> doesn't need it |
| Design a second button | .tuc-btn with is-outline, is-ghost, is-icon, is-sm — including inside a table | A custom class only for positioning |
| Write the placeholder of a masked field | Leave it empty | It comes from the format: data-tuc-mask="cpf" already starts with 000.000.000-00, and data-tuc-mask="brl" with R$ 0,00 |
Load from the CDN with @latest | Pin the version: tucano@v0.37.2 | jsDelivr caches @latest for a long time and silently serves an old build |
Import from tucano through npm and expect fields to set themselves up | Tucano.init(document), or import tucano/auto | Through npm auto-init isn't included, so the bundler keeps only what was imported; tucano/auto initializes and listens to HTMX like the CDN script |
Read e.target.name in the date picker's tucano:change | e.detail.iso, or the <input type="hidden"> next to it | The date picker moves the name to the hidden input with the ISO value; the visible field has no name. In the select, mask and upload the name stays on the target |
| Write CPF validation, currency or date formatting by hand | Tucano.mask, Tucano.dates and Tucano.color | They're the same modules the components use, exposed as API: validateCpfCnpj, applyCurrency, format, parseUserInput, isDark |
| Build a custom floating notice for the server's response | Django's messages in <div data-tuc-toast>, or the HX-Trigger header with tucano:toast | The toast already handles stacking, pausing on focus and the regions the screen reader announces; the HTMX event is listened to automatically |
Publish what came from the editor with |safe directly | Sanitize on the server first | The editor's filter protects the editor, not the published page |
Expect HTML inserted through fetch or innerHTML to initialize itself | Call Tucano.init(node) on the new fragment | The automatic setup covers load and every htmx:afterSwap; the library doesn't see any other insertion path |
Remove data-tuc-ready | Keep the attribute | It's what stops autoInit from setting up the same field twice |
When in doubt, the generated reference wins
If a prose example and the "Referência completa" (full reference) section of llms.txt disagree, the reference wins: it's extracted from the code on every build. Ask the AI to copy each section's example instead of guessing, and to check every option against that list.