{% extends 'django_cotton_gallery/base.html' %} {% load catalog_tags i18n %} {% block title %}{% trans "Documentation" %}{% endblock %} {% block content %} {% url 'django_cotton_gallery:get_started' as get_started_url %}

{% 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 %}

{% 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" %}

{% endblock %}