{% trans "Reference" %}
{% trans "Documentation" %}
{% blocktranslate with url=get_started_url %}Everything that's configurable. Configuration covers the consumer settings you put in settings.py — file location, ordering, asset injection. Annotations covers the {# @prop ... #} grammar the gallery's parser reads to build interactive controls. New here? Start with Get started.{% endblocktranslate %}
{% trans "Configuration" %}
{% blocktranslate %}Every setting below is read from your project's settings.py. None of them are required — the gallery boots with sensible defaults. Override only what you need.{% endblocktranslate %}
{% trans "Cotton & component paths" %}
| {% trans "Setting" %} | {% trans "Type" %} | {% trans "Default" %} | {% trans "Description" %} |
|---|---|---|---|
COTTON_SNAKE_CASED_NAMES |
bool |
True |
{% blocktranslate %}Cotton's own setting. The gallery indexes both styles transparently — the Insights page surfaces a warning if you have hyphens in filenames AND True (the one combo cotton can't resolve, since it would look up foo_bar.html for <c-foo-bar />).{% endblocktranslate %} |
COTTON_DIR |
str |
"cotton" |
{% blocktranslate %}Cotton's own setting. Subfolder under each templates/ root that holds the components — the gallery just reads it.{% endblocktranslate %} |
{% trans "Catalog ordering & filtering" %}
{% blocktranslate %}Top-level folders inside COTTON_DIR become categories (atoms, molecules, etc.). One level deeper become subcategories (atoms/ui, atoms/form, etc.).{% endblocktranslate %}
| {% trans "Setting" %} | {% trans "Type" %} | {% trans "Default" %} | {% trans "Description" %} |
|---|---|---|---|
DJANGO_COTTON_GALLERY_EXCLUDED_CATEGORIES |
list[str] |
() |
{% blocktranslate %}Top-level folders to hide from the index entirely. Folders whose name starts with _ are excluded automatically.{% endblocktranslate %} |
DJANGO_COTTON_GALLERY_CATEGORY_ORDER |
list[str] |
() |
{% blocktranslate %}Categories pinned to the top of the sidebar in this exact order. Anything not listed sorts by DJANGO_COTTON_GALLERY_CATEGORY_SORT.{% endblocktranslate %} |
DJANGO_COTTON_GALLERY_CATEGORY_SORT |
"asc" | "desc" |
"asc" |
{% blocktranslate %}Sort direction for categories not listed in DJANGO_COTTON_GALLERY_CATEGORY_ORDER.{% endblocktranslate %} |
DJANGO_COTTON_GALLERY_SUBCATEGORY_ORDER |
dict[str, list[str]] |
{} |
{% blocktranslate %}Per-category mapping of subcategory names pinned to the top. Example: {'atoms': ['ui', 'form']}.{% endblocktranslate %} |
DJANGO_COTTON_GALLERY_SUBCATEGORY_SORT |
"asc" | "desc" |
"asc" |
{% blocktranslate %}Sort direction for subcategories not listed in DJANGO_COTTON_GALLERY_SUBCATEGORY_ORDER.{% endblocktranslate %} |
{% trans "Asset injection — external URLs" %}
{% blocktranslate %}For external stylesheets and scripts (Leaflet, HTMX, Alpine, your compiled bundle), pass URLs as lists. The gallery auto-detects known libraries from these URLs to render the dependency badges in the hero.{% endblocktranslate %}
| {% trans "Setting" %} | {% trans "Type" %} | {% trans "Default" %} | {% trans "Description" %} |
|---|---|---|---|
DJANGO_COTTON_GALLERY_EXTRA_CSS |
list[str] |
() |
{% blocktranslate %}URLs (static or absolute) loaded in the <head>.{% endblocktranslate %} |
DJANGO_COTTON_GALLERY_EXTRA_JS |
list[str] |
() |
{% blocktranslate %}URLs loaded before </body>.{% endblocktranslate %} |
{% trans "Asset injection — inline HTML via partials" %}
{% blocktranslate %}For inline HTML/JS (Tailwind play-CDN config, preconnect hints, HTMX/Alpine bridges, env keys), drop a template partial in your project's templates directory. The gallery picks it up automatically — no settings change.{% endblocktranslate %}
| {% trans "Path" %} | {% trans "Where it renders" %} | {% trans "Use it for" %} |
|---|---|---|
templates/django_cotton_gallery/_extra_head.html |
{% blocktranslate %}End of <head>{% endblocktranslate %} |
{% blocktranslate %}Preconnects, Tailwind config, design-token CSS variables, env-driven window.* globals (API keys, feature flags).{% endblocktranslate %} |
templates/django_cotton_gallery/_extra_body.html |
{% blocktranslate %}Just before </body>{% endblocktranslate %} |
{% blocktranslate %}HTMX/Alpine bridges, post-mount config, anything that has to run after the components mount.{% endblocktranslate %} |
{% blocktranslate %}Recommended: use the partials approach for inline HTML — it keeps your settings.py minimal and lets your editor syntax-highlight the markup. The settings-based EXTRA_CSS/EXTRA_JS approach still works and is the way to go when you want each URL to surface as a dependency badge in the hero.{% endblocktranslate %}
{% blocktranslate %}Both partials are optional. Drop the file in to add the inject, delete it to remove it. The gallery uses a custom {% maybe_include %} templatetag so missing files are silently skipped.{% endblocktranslate %}
{% blocktranslate %}Example: _extra_head.html{% endblocktranslate %}
{% trans "A typical head partial for a project that runs Tailwind via the play-CDN and exposes an API key to the components:" %}
{% codeblock %}{% verbatim %}{# templates/django_cotton_gallery/_extra_head.html #}
{% endverbatim %}{% endcodeblock %}
{% blocktranslate %}Example: a working settings.py{% endblocktranslate %}
{% blocktranslate %}Only the gallery's own settings are shown — cotton's settings (COTTON_SNAKE_CASED_NAMES, COTTON_DIR) belong to django-cotton, not to this gallery, and are documented in cotton's docs.{% endblocktranslate %}
{% codeblock %}{% verbatim %}# settings.py — django-cotton-gallery configuration
DJANGO_COTTON_GALLERY_CATEGORY_ORDER = ["atoms", "molecules", "icons"]
DJANGO_COTTON_GALLERY_SUBCATEGORY_ORDER = {
"atoms": ["ui", "form", "feedback", "display", "table"],
"molecules": ["navigation", "form", "display", "overlay"],
}
DJANGO_COTTON_GALLERY_EXTRA_CSS = [
"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css",
]
DJANGO_COTTON_GALLERY_EXTRA_JS = [
"https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js",
"https://unpkg.com/alpinejs@3.15.11/dist/cdn.min.js",
"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js",
]{% endverbatim %}{% endcodeblock %}
{% trans "Language switcher" %}
{% blocktranslate %}The gallery ships with translations for English, Spanish, Basque, and French. The dropdown in the sidebar footer only appears when your project has Django's i18n URL and middleware wired — without them, Django can't persist the chosen language, so the gallery hides the control to avoid a dead button.{% endblocktranslate %}
{% codeblock %}{% verbatim %}# urls.py
from django.urls import include, path
urlpatterns = [
path("i18n/", include("django.conf.urls.i18n")),
# ... your real app URLs
]
# settings.py
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
# ... rest as you had it
]{% endverbatim %}{% endcodeblock %}
{% blocktranslate %}Order matters: LocaleMiddleware must come after SessionMiddleware and before CommonMiddleware. To expose more or fewer locales, set LANGUAGES in your settings.py — the gallery picks up whatever is configured.{% endblocktranslate %}
{% trans "Lint in CI" %}
{% blocktranslate %}The same lint engine that powers the in-app Lint report ships as a Django management command. Wire it into CI to fail builds on annotation regressions:{% endblocktranslate %}
{% codeblock %}{% verbatim %}python manage.py cotton_lint # exit 1 if any errors
python manage.py cotton_lint --warnings-as-errors # stricter — also fail on warnings{% endverbatim %}{% endcodeblock %}
{% blocktranslate %}Same rules and severities as the web report. The CLI's exit-code logic considers errors and (with the flag) warnings; hints are surfaced in the web report but don't affect CI exit codes.{% endblocktranslate %}
{% trans "Annotations" %}
{% blocktranslate %}The gallery reads {# @prop ... #} comments at the top of each Cotton template to build the props table and the controls panel. The grammar below covers every annotation feature.{% endblocktranslate %}
{% trans "Anatomy" %}
{% blocktranslate %}One @prop comment per piece of API, then <c-vars> with defaults, then your markup. The shape is always {# @prop name:type | filter | ... #}.{% endblocktranslate %}
{% codeblock %}{% verbatim %}{# @prop name:text | default:"World" | description:"Who to greet" #}
Hello, {{ name }}!
{% endverbatim %}{% endcodeblock %}
{% trans "Prop types" %}
{% trans "The type after the colon decides which control the gallery renders." %}
| {% trans "Type" %} | {% trans "Renders" %} | {% trans "Example" %} |
|---|---|---|
text | {% trans "Text input" %} | label:text | default:"Hello" |
number | {% trans "Number input" %} | :count:number | default:5 |
boolean | {% trans "Toggle" %} | loading:boolean | default:False |
select[…] | {% trans "Dropdown" %} | variant:select['sm','md'] | default:"md" |
{% trans "Filters" %}
{% trans "Pipe-separated modifiers. Order doesn't matter." %}
| {% trans "Filter" %} | {% trans "Example" %} | {% trans "What it does" %} |
|---|---|---|
default | | default:"primary" | {% trans "Default value. Strings quoted, numbers and booleans bare." %} |
description | | description:"Helper text" | {% trans "Shown next to the control." %} |
required | | required | {% blocktranslate %}Marks the prop as mandatory. Only kicks in when there's no default.{% endblocktranslate %} |
deprecated | | deprecated:"Use new-api" | {% trans "Strikes the prop through and shows the migration note." %} |
hidden | | hidden | {% trans "Removes the prop from the controls panel." %} |
example | | example:"bg-brand-500" | {% trans "Sample value shown next to the control." %} |
{% blocktranslate %}Dynamic props (the : prefix){% endblocktranslate %}
{% blocktranslate %}Cotton evaluates the value as a Python expression when the attribute name starts with :. Use it for numbers, booleans, lists — anything that isn't a plain string.{% endblocktranslate %}
{% codeblock %}{% verbatim %}{# @prop :count:number | default:0 #}
{% endverbatim %}{% endcodeblock %}
{% trans "Slots & triggers" %}
{% blocktranslate %}Use @slot to describe the default slot. For components that open something (modal, drawer), use @trigger instead.{% endblocktranslate %}
{% codeblock %}{% verbatim %}{# @slot Click me — Default content for gallery preview #}
{# @trigger — Element that opens the dialog #}{% endverbatim %}{% endcodeblock %}
{% trans "Playground features" %}
{% trans "Every component detail page is a live playground. Beyond the props panel, several features make iteration faster:" %}
{% trans "Variants matrix" %}
{% blocktranslate %}Switch the preview into a grid that renders every combination of the component's discrete props at once. The first two enum or boolean props become the Y/X axes; everything else stays consistent across cells. Useful for QA, visual regression, and seeing the whole API at a glance.{% endblocktranslate %}
{% blocktranslate %}Toggle via the View switcher in the preview toolbar (single-frame icon ↔ grid icon). Cells lazy-load via IntersectionObserver so dense matrices don't fire 30 fetches at once.{% endblocktranslate %}
{% trans "Shareable URL state" %}
{% blocktranslate %}Every change in the controls panel writes the non-default values to the URL via history.replaceState. Copy the URL, send it, and the recipient lands on the same component with the exact same configuration — no description needed for bug reports or design reviews.{% endblocktranslate %}
{% codeblock %}{% verbatim %}# Bug report URL — variant=danger, size=lg, loading=true
/django-cotton-gallery/atoms/ui/button/?variant=danger&size=lg&loading=true{% endverbatim %}{% endcodeblock %}
{% trans "Pass-through attrs control" %}
{% blocktranslate %}Components whose body uses {{ attrs }} or :attrs="attrs" accept arbitrary HTML attributes that aren't declared as <c-vars> props — disabled, id, data-*, aria-*, style, Alpine x-* directives, HTMX hx-* attributes, etc. The gallery auto-detects this and shows an Extra attributes input below the declared props.{% endblocktranslate %}
{% blocktranslate %}Type to filter the suggestion popover. ↓/↑ to navigate, Tab or Enter to insert the highlighted token. Tokens with ="" park the caret inside the quotes so you can type the value immediately. Whatever you type lands literally on the rendered HTML.{% endblocktranslate %}
{% blocktranslate %}Heads up: class may duplicate when the component's root element already declares one — HTML5 keeps the first class attribute and ignores the rest. Use style, data-*, or any non-conflicting attribute for visual overrides.{% endblocktranslate %}
{% trans "Sidebar typeahead" %}
{% blocktranslate %}Press / from anywhere to focus the search. Start typing — a dropdown appears with name + path + description for every match. Hit Tab to autocomplete the input with the first match (or with whatever suggestion you've focused via ↓). Enter activates the first match; Esc clears.{% endblocktranslate %}
{% trans "Live syntax highlighting" %}
{% blocktranslate %}Both the slot textarea and the extra-attrs editor render token colors AS you type — HTML tags, Cotton component names, attribute names, strings, Django {% %} blocks, and CSS properties inside style="…" all get their own color. The slot editor uses an overlay technique; the attrs field is a contenteditable div so the cursor sits on top of the colored text directly.{% endblocktranslate %}
{% trans "CSS autocomplete inside style attributes" %}
{% blocktranslate %}When the caret enters a style="…" attribute, the popover switches from HTML-attribute suggestions to CSS-aware ones. It detects three contexts:{% endblocktranslate %}
- {% blocktranslate %}Property name after
;or at the start — suggestsdisplay,padding,color, etc.{% endblocktranslate %} - {% blocktranslate %}Property value after
:— suggests keyword values for the current property (flex,none…), color names, or shorthand pieces (e.g.border: 1px solid redclassifies tokens as you type so it only offers what's missing).{% endblocktranslate %} - {% blocktranslate %}Length unit after a number — typing
2offerspx/rem/vh/dvh/etc; typing2dfilters to2dvh/2dvw(matching prefix).{% endblocktranslate %}
{% trans "Maximize editor modal" %}
{% blocktranslate %}Both the slot textarea and the extra-attrs field have a maximize button (the diagonal-arrows icon) that opens the editor in a full-screen modal. The modal preserves the editor's contents, caret position, and bindings — autocomplete, syntax highlight, formatter, all keep working. The header has Format and Reset buttons; the footer shows live char/line counts and keyboard shortcuts. Press Esc or click the backdrop to close.{% endblocktranslate %}
{% trans "Sidebar collapse to rail" %}
{% blocktranslate %}On desktop, a small toggle on the sidebar's right edge collapses it to a 4rem icon rail (logo + Empezar + Documentación + theme/lang/github). The component tree hides — to navigate components, expand again. The state persists across sessions via localStorage, applied before first paint to avoid a layout flash.{% endblocktranslate %}
{% trans "Rules & gotchas" %}
- {% blocktranslate %}Strings in filter values are quoted (
default:"primary"). Numbers and booleans are bare (default:5,default:False).{% endblocktranslate %} - {% blocktranslate %}If
<c-vars>uses:prop(dynamic), the matching@propMUST also start with:.{% endblocktranslate %} - {% blocktranslate %}
| requiredonly takes effect when there's no| default:. If both are present, default wins.{% endblocktranslate %} - {% trans "Annotations are plain Django comments — Cotton and Django ignore them at render time. Only the gallery's parser reads them." %}