Examples
Other
Ajax buttons
The markup ajax adds to a page, which no template holds until something is pressed: the five progress indicators, the throbber a select or a check box carries, the wrapper buttons add rows to and remove them from, the modal and off canvas dialogs, the messages a command hands to the page, and the error state an element is rebuilt in.
Autocomplete and selection widgets
The widgets contrib puts over a select or an autocomplete, from six modules at once, so they can be read against each other and against the three shapes core ships. Select2, Select A11y, Autocomplete Deluxe, Active Tags, Select or Other and one Chosen widget for the comparison: they solve the same problem and produce markup with nothing in common, so the theme has to meet each of them separately. Nothing is saved. The option lists are made up, so most of the page renders the same anywhere; the widgets that query the site for their suggestions say so, and their results are real.
Basic pages
Five bodies of a basic page, and the only markup in this library the theme does not control: whatever an author typed into CKEditor, reached through one class, `.text-formatted`. Plain prose, code blocks, links dressed as buttons, tables drawn in the editor, and the wrappers an author adds expecting a rule to exist. The classes are real -- they were read out of the `html_tag_usage` report over four of the sites -- but the content is made up and nothing is saved.
Chosen select widgets
The widget the Chosen module puts over a select, in every shape it takes: single and multiple, placeholder and deselect, grouped and flat, searchable and not, capped, disabled, in error, and opted out for comparison. A Chosen dropdown closes on blur, so it is gone the moment you look at anything else -- the inspector included. The toolbar at the top of the page pins every widget open and keeps it interactive, and the widgets in "The drop, held open" are already open when the page loads, each in a result state that is otherwise a moving target: highlighted, searched, and empty. Nothing is saved and every option list is made up.
Components, content
Every story of every component under `mantra_starter/components/content`: the content list, its heading and its items, including the column counts and the three line heights the items are built at. They are shown together because they are used together -- a list is its heading and its items, and the spacing only reads once all three are on the page. Each block is a `*.story.yml` committed beside its component. Every story is made up and nothing is saved.
Components, general
Every story of every component at the top of `mantra_starter/components` -- the pieces that are not tied to one part of the page: the branding, the buttons, the cards and the grid they sit in, headings, links, the pager, the search box, tables, tags and the rest. The component library at `/ui-components` is still the place to read one component, with its props and its schema; this is the page that puts them next to each other, in the theme, at the width the theme serves. Nothing here is authored: each block is a `*.story.yml` committed beside its component, so the page and the library cannot disagree. Every story is made up and nothing is saved.
Components, layout
Every story of every component under `mantra_starter/components/layout`: the page itself, the banner and the footer it is built from, and the column-with-a-sidebar the content region is laid out in. These are the components that render a whole page, so the preview ends up with two of every landmark -- a banner inside the banner, a main inside the main. That is the preview and not the theme. The sidebar component is shown inside a container query context of its own, because its two-column rule is a container query and a container query only ever matches an ancestor. Each block is a `*.story.yml` committed beside its component, and nothing is saved.
Components, navigation
Every story of every component under `mantra_starter/components/navigation`: the menu and its links, the wrapper the menu blocks are placed in, the primary navigation with its collapsible parents, the responsive menu and its toggle, and the tabs. Navigation is the part of the theme with the most components that only make sense beside each other -- a menu link inside a menu inside a responsive menu -- so this is the page to read the nesting on. The primary navigation opens its parents with `details` and `summary` rather than with a script, so the open and closed stories are both the real thing with no JavaScript running. Each block is a `*.story.yml` committed beside its component, and nothing is saved.
Date and time widgets
Every date, range, duration and recurrence widget installed on this site, built from the real field widget plugins rather than rebuilt by hand -- a date widget is rarely one element, and copying it would be copying the thing under test. Core's date, range, datelist and timestamp widgets; the recurring ones from Date Recur and its four modular rewrites of the same rule; Smart Date, which thinks in durations rather than end times; Duration Field and Interval, which store a length with no start; and the pickers from Flatpickr, Duet, Single Date Time and Datetime Range Popup. The field definitions are made up on the spot, so no fields need to exist and nothing is saved. A widget whose module is not installed is left out rather than shown broken.
Dropbuttons
One control with several answers, in every setting it is used in: the operations column of a listing of config entities and of a listing of content, the actions of a form as a split button of links and as one of submit buttons, and the views UI, which restyles them on top of whatever the theme did. A dropbutton is the only back office widget that has to open over the page rather than inside it, so the first thing to check is that nothing below it moves. Nothing is saved and every row is made up.
Drupal logo
The `mantra_starter:drupal_logo` component, dressed up. A dressing room to try any style with any hat and any pair of glasses, then every style, every hat and every pair of glasses on their own, and every style in costume. The hats and glasses are `drupal_logo_hat` and `drupal_logo_glasses`, the accessories in the component's own directory, reached through the logo's `hat` and `glasses` props. Every option is read off the component's definition, so a style or a variant added there appears here. Nothing is saved.
Entity reference form
Content edit form with single and multiple value entity reference fields, rendered with each of the reference widgets: autocomplete, autocomplete with tagging, select list, check boxes/radio buttons, and the simple and complex inline entity forms. The values are dummy ones, so the page renders the same on every site.
Form actions
The row every form ends with, in five shapes: the one core puts at the foot of an entity form, one of everything that can turn up in such a row -- which is also the test of what happens when it wraps -- the dropbutton buttons are folded into when they are given a `#dropbutton`, the actions of a confirmation form, where the primary button is the destructive one, and an actions element on a line with the field it filters. Nothing is saved: each button that submits says which one was pressed.
Formatted text selection
The widget a formatted text field is edited with, in the four states it comes in: a field allowed one format, a field allowed several, a field on a format with no text editor behind it, and a field whose stored format is not one it allows any more. Three of the four are altered by `mantra_field`, which names the format where core hides the selector, keeps a dropped format selectable, and puts the 'About text formats' link back under the control it explains. Nothing is saved, and what the page shows depends on the formats the account reading it may use.
Front pages
The front pages of `classes`, `contrib`, `retrograde` and `architect`, rebuilt out of dummy content. Every other example here is one piece of markup; a front page is a stack of layout builder sections, so what there is to get right is the composition -- how much room a section takes, what the grid on its region does at each width, and whether two blocks of different kinds sit properly next to each other. The layouts, block plugin ids and views are the real ones the sites saved, so the theme picks the same templates. Nothing is saved and the content is made up.
Hierarchical tables
The other half of the hierarchical tableselect example: the administrative tables of a tree that select nothing, which is most of them. A book outline carried by indentation and dragged into shape, with a text field in its first cell; a manage display form, whose tree is region grouping rather than indentation and whose rows carry five widgets each; and the same tree printed rather than administered. The trees are made up and nothing is saved.
Hierarchical tableselect
The two places a table lists a tree with a column of check boxes: the terms of a vocabulary and the links of a menu. Three tables, because the shape takes two elements to build -- a real tableselect over a term tree, with a selected row, a disabled row and the per-level indentation markers; the overview form of a menu, whose rows are form elements and whose check box column has to be built by hand; and the same element with multiple turned off, which gives radio buttons and no select all cell. The trees are made up and nothing is saved.
Item lists
The smallest theme hook anybody has to style, and the one whose markup differs most between core and this theme: `mantra_starter` keeps the `div.item-list` wrapper it inherited from `starterkit_theme` and styles everything through it, where core emits no wrapper at all. The first half of the page is the hook -- what each of its seven variables does to the markup, and the four shapes core puts a list into. The second is the stylesheet: the grid wrapper, `break-inside: avoid`, `--item-list-line-height` and the adjacent-sibling margin, each rendered twice, with the declaration and without it. Every item is made up and nothing is saved.
Layouts under long and wide content
The layouts of the theme holding five things chosen to break them: a string with nowhere to wrap, a table wider than its column, the same sentence inside a text format and outside one, an image and an iframe that arrive knowing how wide they want to be, and a select sized by its longest option. There is deliberately nothing to see -- no section may move the document sideways at any width, so the check is the bottom of the window rather than the columns. The one exception is the pair of boxes holding the same sentence, where only the formatted one breaks its long word.
Menu and taxonomy overview
The two forms every site administers a tree with, built with the properties core sets, in the same places and with the same values: the links table of a menu and the terms table of a vocabulary. They are not the same table -- a vocabulary prints the state of a term in words, leaves the parent field visible, writes the depth back and is paged, so its table also holds the rows either side of this page, a second button and a pager. The trees are made up and nothing is saved.
Radios and checkboxes
The two boolean groups in every layout they get asked for: stacked and on one row, with a description under every option, under some of them, and under none. Both elements produce the same markup -- a fieldset around one .form-item per option -- so they are on one page and meant to be read against each other. The interesting half is the descriptions: they are legal on every option and core renders them without complaint, but they arrive as a third child of a .form-item that is usually laid out expecting two, and they are wider than the label they explain. Formtips is installed on this site and most of the others, and it takes every one of those descriptions out of the flow and leaves a tooltip trigger in its place, so each described set is shown twice: once as the site renders it, and once inside a .no-formtips container -- a selector added to formtips.settings for exactly this -- where the descriptions stay where core put them. The pairs are the point. With the descriptions visible the theme's two-column grid comes apart: the description sizes the max-content column, and the label of every option is pushed the width of the longest description away from the input it belongs to. The last two sections cover the states that change the markup, and the three properties that look as though they would put a set on one row -- only one of which arrives anywhere the CSS can see it. Nothing is saved. One set is required, so submitting the form empty shows the real error state rather than a copy of it.
Sidebar layout
The other kind of sidebar, and the one it is easy to confuse with the first: not a region of the page but a region of the layout the content is laid out with, inside the content region and inside the same block as the body beside it. It holds what the thing is -- its state, what it belongs to, when it last changed -- rather than the navigation of the site. The layout is a real layout plugin asked of the plugin manager, with the core two column layouts standing in for a theme that ships none, and field blocks in its regions.
Sidebar page and layout
The two sidebars on one page, the way a page of a real site is usually built: a page sidebar on the left, outside the content, and a layout sidebar on the right, inside the content region. The second measurement depends on the first -- the layout has to place its column in what the page sidebar left rather than in the width of the window, which is where a layout deciding on a media query and one deciding on a container query stop agreeing. Narrowing the window then crosses two thresholds rather than one.
Sidebar regions
A whole page rather than a piece of one: the sidebar regions of the theme are filled, so the content sits between a navigation column and a column of page metadata. The blocks are built for the example rather than placed, so nothing has to be placed to see the page, but they are ordinary block render arrays in ordinary regions. The content is deliberately long -- a sidebar only shows whether it scrolls with the page or stays in view once the content beside it is taller than the window.
Table column widths
What every other table example has to settle and none of them shows on its own: how wide each column ends up. The theme leaves tables at auto layout and gives a column a role rather than a width, so a select moves each of the four roles onto a different column and the layout can be watched rather than described, with the same table repeated underneath with nothing applied. Then the three things the roles rest on: a table too wide for the page, cells with nowhere to break, and a header that stays put while its table scrolls both ways. The data is made up and nothing is saved.
Typography
Every rule the theme's generated typography stylesheet contains, quoted beside markup it applies to. `mantra_starter` styles formatted text with UnoCSS's `presetTypography` under `selectorName: 'text-formatted'`, so what styles a body of text is generated, is in no file anybody edits, and can only be read by opening `dist/unocss.css` after a build -- this page is that file, laid out. It also covers the thirty-six custom properties the colour scheme passes through, the generated rules that match nothing, and the twelve places where these rules meet the theme's bare element styles. The content is made up and nothing is saved.
User forms
The three forms a visitor meets on their way into a site: log in, register, and request a password reset. Each one reproduces the elements core builds for an anonymous visitor, so the markup matches the real page, but none of them authenticates, creates an account or sends a mail.
Views, as the sites run them
Five of the more complicated views the sites run, rebuilt out of dummy data: the task list of `contrib`, a content overview, the course search of `classes`, the chores dashboard of `inventory` and the project grid of `portfolio`. Between them they cover a view over a search index, a link from one display to another, rows rendered inline, a bulk form and an operations dropbutton in a sortable table, a self-submitting exposed form, and a responsive grid with no pager. Each is a real view built in memory and never saved, listing two dummy rows: the query is built but never sent, so the widgets are here to be themed rather than used.
Views exposed forms
The top of a view, in six combinations: a header on its own, exposed filters on their own, exposed sorts on their own, all of it at once with the reset and pager controls, grouped filters, and the sort links a table style puts in its header cells. Every variation is a real view, built in memory and never saved, listing the same dummy people. Nothing is queried, so applying a filter changes nothing, but the widgets come back in the state they were submitted in -- and they share the query string, so applying one is visible in the others that expose the same identifier.