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