{% 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 %}
{% blocktranslate %}Strict components (@strict){% endblocktranslate %}
{% blocktranslate %}Add {# @strict #} to declare a closed prop set — every prop is listed via @prop. The detail page shows a @strict badge, and the linter turns two things into errors: an undocumented <c-vars> prop, and a stray {{ attrs }} (which would let arbitrary attributes through, contradicting the closed set).{% endblocktranslate %}
{% codeblock %}{% verbatim %}{# @strict #}{% endverbatim %}{% endcodeblock %}
{% blocktranslate %}Intentionally unused (@ignore-unused){% endblocktranslate %}
{% blocktranslate %}Insights flags zombies — components nothing references (when scanning is on). Add {# @ignore-unused #} to a component that's deliberately unreferenced (a library component, a WIP) so it's excluded from the zombie list. Shows an @ignore-unused badge on the detail page.{% endblocktranslate %}
{% codeblock %}{% verbatim %}{# @ignore-unused #}{% 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 "Preview background" %}
{% blocktranslate %}The Bg bar in the preview toolbar switches the backdrop between checkered, white, dark, and brand. Hit Edit to turn the last three swatches into color pickers and recolour them to your app's theme — the choice previews live and is only saved on Done (persisted in localStorage); Reset restores the defaults.{% 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 "Language-aware syntax highlighting" %}
{% blocktranslate %}The Source tab, the Cotton-tag preview, and the live editors (slot textarea + extra-attrs field) all colour the full template stack: HTML tags, Cotton component names (<c-…>), Django {% %} / {{ }} blocks and filters, HTMX (hx-*) and Alpine (x-* / @ / :) attributes — with the Alpine value tokenised as JavaScript — embedded <script> JS and <style> CSS, and the {# @… #} annotation DSL. 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." %}