//! Widgets: named assemblies of things the description already says. //! //! The third tier, between a primitive and `Region::Bespoke`. A primitive is //! drawn from scratch by every renderer and has to be something every host can //! honour; a bespoke region is one app's own and carries no contents the //! description knows. A widget is neither: a *name* over an assembly of members //! the vocabulary already has. //! //! Full argument: wiki `widget-tier`. //! //! # What is left here //! //! Glue, and only glue. The widgets themselves live in `quasi-basics`, which is //! where the carousel went once it had been proved against three real pages. //! Each module here is an Askama entry point: it turns whatever the templates //! happened to need into the widget's own types, renders the fragment, and //! stops. //! //! A widget that has never rendered anything is a guess about what the assembly //! needs, so a new one is welcome to start here and leave later. The carousel //! found a missing primitive on its way through //! (`makeover_layout::Image`, 0.21.0), which is the argument for proving one in //! an app before it becomes shared. //! //! # A primitive's glue is a sibling of this directory, never a member //! //! Ruled by Max 2026-08-21 (`27d5e5b8`), and written here because this doc is //! what gets read when someone asks where a described fragment goes. //! //! **A described fragment inside an Askama page is legitimate at any //! granularity the vocabulary already names, primitive included.** One `Field` //! qualifies; so does one `Act`. The question was whether that made a single //! primitive a widget, and the answer is that it does not — it makes it a //! primitive with an entry point. So the glue module sits at `src/quasi/`, //! beside `rich_field.rs`, `upload_field.rs` and `export_act.rs`, and this //! directory keeps meaning what the paragraphs above say it means: a name over //! an assembly. The "misuses the word widget" objection is answered by //! placement rather than by declining the pattern. //! //! What it buys is that a shared control converts at its own granularity //! instead of once per parent. `export_act.rs` is the first one taken: five //! Export CSV buttons in four templates, described once. The same applies to //! the shared act rows — `partials/link_row.html`, `partials/tag.html`, and the //! two identical delete buttons in `tabs/project_content.html` and //! `tabs/project_blog.html`. //! //! ## What it costs, stated so it is not rediscovered //! //! A live visual change on a page no flag gates. The templates write //! `class="btn-secondary text-sm"` and a described act emits `class="button"`, //! so the control lands on the design system's button rather than the app's: //! `--surface-raised` and `--bevel-raised` where `.btn-secondary` sets //! `--surface-sunken`, `--shadow-raised` and its own padding and font size. //! That is the swap, it is deliberate, and it wants an eyeball rather than a //! test. //! //! It is not, any more, a loss of the design system's button altogether. //! `35756077` measured that `style.css` is unlayered and outranks //! `@layer makeover` at any specificity, so a bare `button { }` rule was taking //! the surface, the bevel **and the tone** off every described act. Fixed by //! the `revert-layer` handoff at `style.css:473`. A described act that reaches //! for `Tone::Danger` gets it now, which it did not before 2026-08-21. pub mod carousel;