rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1{
2 "families": {
3 "choices-and-actions": {
4 "title": "Choices and actions",
5 "blurb": "The controls that pick from options, and the rows that end a form. Each one sits in a padded rst-box, because that is the card a form goes in."
6 },
7 "date-and-time": {
8 "title": "Date and time",
9 "blurb": "The four fields datetime.js enhances, each keeping its native input underneath, so a reader with no script gets the plain control and not a broken one."
10 },
11 "display": {
12 "title": "Display",
13 "blurb": "The small pieces of vocabulary a screen is assembled from: status, identity, progress and the one alert shape."
14 },
15 "form": {
16 "title": "Form",
17 "blurb": "The fields a person types into, in the states a real form puts them in. Each one sits in a padded rst-box, because that is the card a form goes in."
18 },
19 "formats": {
20 "title": "Dates, numbers and names",
21 "blurb": "How to show dates, numbers, money, names and addresses, and which ones people mix up."
22 },
23 "list-screen": {
24 "title": "List screen",
25 "blurb": "Generic list screen with title, toolbar, rows that work desktop and mobile."
26 },
27 "primitives": {
28 "title": "UI primitives",
29 "blurb": "The shapes a component cannot be, because they wrap a body only the caller knows: cards, data grids, menus and the shells' own chrome."
30 },
31 "route": {
32 "title": "Route",
33 "blurb": "The pieces that are a whole response rather than a piece of one."
34 },
35 "screens": {
36 "title": "Screens",
37 "blurb": "Whole screens built from the components. Start your own from one of them."
38 },
39 "shells": {
40 "title": "Shells",
41 "blurb": "The page frames an app is built in: a column, a top bar, a sidebar, a console and a stage. Each opens full width."
42 }
43 },
44 "examples": {
45 "actor": {
46 "blurb": "Who did something, as words: a person's name, or the agent and the person it acted for. No badge and no icon.",
47 "states": {
48 "an-agent-acting-for-a-person": "The agent's name, then the person it acted for. When the app itself did something, show nothing."
49 }
50 },
51 "api-tokens": {
52 "blurb": "The API token page: the one-time reveal, the create form, the list and the revoke buttons.",
53 "states": {
54 "four-tokens-two-of-them-live": "The revoke forms post to /tokens/revoke, /tokens/revoke-agent and /tokens/revoke-all, under the page's own path. Point them wherever your app handles them.",
55 "just-created-the-secret-shown-once": "Show this in the response to the create form itself, and do not redirect it: the secret is in that response and nowhere else."
56 }
57 },
58 "back-nav": {
59 "blurb": "The link back up one level, above a show or edit screen."
60 },
61 "badge": {
62 "blurb": "An uppercase bordered chip for small static markers. Distinct from status-pill, which is record status."
63 },
64 "box": {
65 "blurb": "The padded section card, and the heading that sits outside it."
66 },
67 "bulk-bar": {
68 "blurb": "Select mode's header strip, replacing the list bar: the count, an escalate link, and the actions menu.",
69 "states": {
70 "three-rows-selected": "The destructive item is last: the first entry is the surrounding form's implicit-Enter default."
71 }
72 },
73 "button": {
74 "blurb": "Buttons come in four variants and three sizes, and a link can look like one. For the spinner a form shows while it submits, see form-foot."
75 },
76 "callout": {
77 "blurb": "The one alert vocabulary: tinted strip, tone icon, optional bold title, one body paragraph.",
78 "states": {
79 "negative-announced-as-an-alert": "role=\"alert\" makes a screen reader announce it as soon as it appears. Keep it for messages that cannot wait."
80 }
81 },
82 "choice-field": {
83 "blurb": "Option cards: the preferred radio or checkbox group. The title stays short; the description explains."
84 },
85 "confirm-form": {
86 "blurb": "The confirm page's action row. Destructive actions get their own route rather than a dialog, so there is nothing to script.",
87 "states": {
88 "ordinary-default-cancel-wording": "The cancel link says Cancel, which is the usual wording. Change it when the form's own words are clearer."
89 }
90 },
91 "detail-list": {
92 "blurb": "Definition list about a record on a detail screen. Mono marks for machine-ish values."
93 },
94 "dropdown": {
95 "blurb": "A zero-JavaScript disclosure: a summary button over a panel of plain links. Put it in a row beside what it acts on, never on a line of its own.",
96 "states": {
97 "menu-idiom": "The details/summary menu behind header overflow menus and a list bar's filter, plus an applied filter as a removable chip."
98 }
99 },
100 "empty-state": {
101 "blurb": "The blank-state card. Empty state with affordances for starting steps."
102 },
103 "error-page": {
104 "blurb": "The whole body of an error response: the status, one honest sentence, a way back, and 500 references an admin can use.",
105 "states": {
106 "500-server-error": "A 500 is the one status with a reference: an operator can grep the logs for it.",
107 "anything-else-the-generic-pair": "Any status that has no wording of its own gets this generic pair: a title that says something is wrong and a body that says to try again."
108 }
109 },
110 "field": {
111 "blurb": "Label, input, optional hint, help and error. The general text-like control, with an explicit ID."
112 },
113 "field-check": {
114 "blurb": "The one toggle mechanism: a real checkbox skinned as a switch, keyboard and screen-reader intact."
115 },
116 "field-date": {
117 "blurb": "A native date input, plus the natural-language combobox datetime.js adds when the input carries data-rst-date. Type \"tomorrow\" into it.",
118 "states": {
119 "enhanced-input-type-tomorrow-or-in-3-weeks": "The words it parses are on data-rst-date-words, here in the page's language. i18n/\u003clang\u003e.json has them in twelve languages, so the field can parse whichever language the page is in.",
120 "plain-the-bare-native-input": "No enhancement attributes at all, so datetime.js has nothing to find. The native picker still opens."
121 }
122 },
123 "field-daterange": {
124 "blurb": "A start and an end as one labelled fieldset. Give every input its own name and id.",
125 "states": {
126 "date-and-time-seeded": "Commit the start with the end still empty and datetime.js fills the end in an hour later. Nothing in the markup does that; the script does."
127 }
128 },
129 "field-datetime": {
130 "blurb": "A date and a time in one input, one value.",
131 "states": {
132 "enhanced-input-type-tomorrow-or-in-3-weeks": "The words it parses are on data-rst-date-words, here in the page's language. i18n/\u003clang\u003e.json has them in twelve languages, so the field can parse whichever language the page is in.",
133 "plain-the-bare-native-input": "No enhancement attributes at all, so datetime.js has nothing to find. The native picker still opens."
134 }
135 },
136 "field-money": {
137 "blurb": "A money amount in one currency. The currency code shows beside the label and is sent with the form, so a currency that changed while someone was typing is caught on save."
138 },
139 "field-select": {
140 "blurb": "The field envelope around a native select. Add data-rst-select and select.js turns it into a filterable combobox; the native select is still what the form submits. Worth it from about ten options.",
141 "states": {
142 "hand-written-with-optgroups": "Grouped choices are optgroups you write yourself. select.js keeps the groups rather than dropping them; a select can also refuse the enhancement outright with data-rst-select=\"false\".",
143 "twelve-options-the-filterable-combobox": "Add data-rst-select from about ten options. Below that, leave it off: search over a handful of items is furniture, not help."
144 }
145 },
146 "field-text": {
147 "blurb": "One labelled text input. The required marker's star is aria-hidden; the input's required attribute is the programmatic signal.",
148 "states": {
149 "the-headline-field": "Give the main field of a form, a title or a name, rst-input=\"primary\" for a more prominent input."
150 }
151 },
152 "field-textarea": {
153 "blurb": "field-text's wrapper around a textarea."
154 },
155 "field-time": {
156 "blurb": "The same envelope around a native time input. The enhancement reads a clock rather than a calendar."
157 },
158 "field-url": {
159 "blurb": "A web address field that accepts example.com with or without https://. Whatever reads the form has to add the scheme and check the address."
160 },
161 "form-error": {
162 "blurb": "A form's summary error line, role=\"alert\". Leave it out when there is no error."
163 },
164 "form-foot": {
165 "blurb": "A form's closing row: one primary submit and an optional cancel link. Cancel is an anchor, because leaving a form is navigation.",
166 "states": {
167 "idle-the-button-before-anything-happens": "A button that CHANGES something gets a loading state; a button that only reveals something, e.g. a disclosure, a dropdown, a tab, does not. busy.js applies that rule to every submit button in every form, with nothing to opt into.",
168 "working-what-busy-js-writes-on-the-way-out": "Only the button that was clicked: every other submit button in the form keeps its name and its value, and the form itself is guarded against a second submit. data-busy=\"false\" opts out, on the form or on one button; data-busy-label replaces the text. With scripts off none of this happens and the form submits exactly as it always did, so a second submit is still your app's job to catch."
169 }
170 },
171 "form-layout": {
172 "blurb": "The attributes that give a form its rhythm and its save bar. They wrap a run of fields you compose yourself."
173 },
174 "format-abbr": {
175 "blurb": "The \u003cabbr\u003e tag shows the full wording when you hover. That does not work on a phone, and screen readers often skip it. Write the words out in full the first time as well."
176 },
177 "format-addresses": {
178 "blurb": "The \u003caddress\u003e tag is not for postal addresses. It is for saying who wrote the page. A delivery address is ordinary text. A list of staff is a list."
179 },
180 "format-dates": {
181 "blurb": "Put a date in a \u003ctime\u003e tag, with a version of the date a computer can read. Write the date itself however your readers expect to see it. Fiesta will not format it for you."
182 },
183 "format-durations": {
184 "blurb": "Use \u003ctime\u003e for a length of time too. The computer-readable version looks strange: PT2H30M means 2 hours 30 minutes."
185 },
186 "format-filesizes": {
187 "blurb": "Show a rounded size like 4 MB, and put the exact number of bytes in a \u003cdata\u003e tag. Screen readers read what is on screen and ignore the tag, so the rounded size has to be useful on its own."
188 },
189 "format-identifiers": {
190 "blurb": "Do not break these up. Order 4471, not order 4,471. Years, version numbers and port numbers are the same. Use rst-mono so they look like codes, and never put one in a \u003ctime\u003e tag, even when it looks like a date."
191 },
192 "format-money": {
193 "blurb": "Store each amount as a whole number of the currency's smallest unit, such as cents, with its currency code beside it. Write it the way the reader's language does. The currency always comes from your data, never from the reader."
194 },
195 "format-numbers": {
196 "blurb": "Break up long numbers so they can be read at a glance: 54,173 in English, 54.173 in German, ٥٤٬١٧٣ in Arabic. Never show a long number as plain digits. In a table, use digits that are all the same width, so the columns line up."
197 },
198 "format-output": {
199 "blurb": "When your page adds something up, put the answer in an \u003coutput\u003e tag. A running total, a converted price, a countdown."
200 },
201 "format-people": {
202 "blurb": "A name can be in any language, including ones that read right to left. When a name sits in the middle of a line, wrap it in \u003cbdi\u003e, or it will scramble the words around it. Use person to show a name with a picture beside it."
203 },
204 "format-ratios": {
205 "blurb": "Use \u003cmeter\u003e to show how full something is. Use \u003cprogress\u003e to show how far a job has got. Always print the number beside the bar. A bar on its own tells a blind reader nothing."
206 },
207 "help": {
208 "blurb": "A bordered question mark linking to a help article. Its CSS tooltip is decoration; the link carries its own full-sentence label."
209 },
210 "job-status": {
211 "blurb": "A background job's progress: a spinner, a line of text and a bar. The data-poll attributes say where a page could fetch a fresh copy and how often. Fiesta ships no script that does that, so wire it up yourself, and leave the attributes off once the job is done.",
212 "states": {
213 "running": "The data-poll attributes are where a script would fetch a fresh copy from, and how often. Nothing in Fiesta does, so on this page it stays running."
214 }
215 },
216 "layout-column": {
217 "blurb": "The plain centred page an app starts on: a skip link, a title, the content column, and a footer row for the language and colour-scheme menus."
218 },
219 "layout-console": {
220 "blurb": "A bar across the top and a navigation rail down the side at once, the shape most admin consoles are. On a phone, the bar's menu opens as a card and the rail is an index page. No JavaScript needed."
221 },
222 "layout-console-page": {
223 "blurb": "A bar across the top and a navigation rail down the side at once, the shape most admin consoles are. On a phone, the bar's menu opens as a card and the rail is an index page. No JavaScript needed."
224 },
225 "layout-sidebar": {
226 "blurb": "A navigation rail beside the page. On a phone, the rail is an index page and every other page has a back control. No JavaScript needed."
227 },
228 "layout-sidebar-page": {
229 "blurb": "A navigation rail beside the page. On a phone, the rail is an index page and every other page has a back control. No JavaScript needed."
230 },
231 "layout-stage": {
232 "blurb": "One card in the middle of a full-page backdrop, for a screen that stands alone. The sign-in screen is what it is for."
233 },
234 "layout-topbar": {
235 "blurb": "Brand, navigation and an account menu across the top, with a footer under the page."
236 },
237 "list-bar": {
238 "blurb": "The toolbar at the top of a list card with search and filter."
239 },
240 "list-bar-search": {
241 "blurb": "Solo search form for search-only lists. An ordinary method=\"get\" form.",
242 "states": {
243 "empty-accessible-name-from-the-catalog": "No placeholder. The input's accessible name is its aria-label, which an app writes in the page's language.",
244 "with-a-query": "The ✕ is a link to the same screen with the query dropped. The status filter rides in a hidden input and survives it.",
245 "with-a-query-on-page-4": "The page number rides in a hidden input, so a new search would carry page=4 into a result set that no longer has four pages. The ✕ link drops it: point it at the list's first page."
246 }
247 },
248 "list-grid": {
249 "blurb": "The real data-table vocabulary: the card sets its columns once, rows only choose cells."
250 },
251 "list-row-action": {
252 "blurb": "One list row: the record's name as the primary link, a meta line, and a secondary action.",
253 "states": {
254 "a-short-status-beside-the-name": "rst-status=\"inline\" keeps a short status on the name's line on a phone. Without it the status moves under the name."
255 }
256 },
257 "list-search-submit": {
258 "blurb": "The search form's real submit button: visually hidden, in the tab order, and it un-hides on keyboard focus.",
259 "states": {
260 "default-label": "Nothing is visible here until you tab into it."
261 }
262 },
263 "locale-menu": {
264 "blurb": "The language switcher: a details menu of one-field POST forms, so the choice is sent to your app and the reader stays on the same path.",
265 "states": {
266 "twelve-languages-current-highlighted": "Each language is a one-field form, so choosing one is a POST your app answers. A static site can use a menu of plain links instead."
267 }
268 },
269 "media-grid": {
270 "blurb": "A grid of image tiles. Every frame is 4:3, so tall screenshots and wide banners sit in one even grid.",
271 "states": {
272 "screenshots-a-logo-and-a-banner": "Give a logo or a banner rst-media-frame=\"contain\". A screenshot keeps the default and shows its top. A tile with no description says \"No description\".",
273 "select-mode-one-tile-selected": "Wrap the grid and a bulk-bar in one form. Each tile's checkbox posts its value under the name \"id\"; rename it if your app expects another. Inside the form, menu items must be links."
274 }
275 },
276 "meter": {
277 "blurb": "A capacity bar with its number.",
278 "states": {
279 "over-budget": "Over budget, the bar is full and the number says by how much: write value=\"100\" and leave the real figure to the text."
280 }
281 },
282 "modal": {
283 "blurb": "A modal is its own URL, not client state: the page underneath, marked inert, with the panel over it and a plain link to close."
284 },
285 "notice": {
286 "blurb": "A one-line confirmation after a redirect: one sentence in a paragraph with role=\"status\". Leave it out when there is nothing to say."
287 },
288 "page-header": {
289 "blurb": "The screen's title bar: an h1, an optional subhead, and a primary action."
290 },
291 "pagination": {
292 "blurb": "A numbered strip of ordinary links, so paging survives JavaScript being off.",
293 "states": {
294 "first-page-default-accessible-name": "The nav's aria-label is the plain word Pagination. Say which list it pages when a page has more than one."
295 }
296 },
297 "person": {
298 "blurb": "Initials avatar, name and email. Quickly identifies people records."
299 },
300 "row-menu": {
301 "blurb": "A row's other actions, behind a ⋮ button. A destructive one links to its confirm page.",
302 "states": {
303 "links-a-form-and-a-destructive-item": "Put the destructive item last and end its label with …. It opens a page that asks before anything is deleted.",
304 "links-only-for-a-list-inside-a-selection-form": "Inside a form, every item must be a link. An action that posts goes through a page of its own."
305 }
306 },
307 "scheme-menu": {
308 "blurb": "The colour-scheme switch: a details menu of one-field POST forms for Light, Dark and System. Posted, the choice goes to your app; standalone, fiesta.js remembers it in the browser.",
309 "states": {
310 "standalone": "No app behind it: the forms have no action, and with data-rst-scheme-store=\"local\" on the \u003chtml\u003e element fiesta.js remembers the choice in the browser. The gallery's own header uses this one.",
311 "three-choices-dark-current": "Each scheme is a one-field form, so choosing one is a POST your app answers, and the page comes back with the chosen scheme marked current."
312 }
313 },
314 "seg-tabs": {
315 "blurb": "A segmented control of links or buttons. The current link is aria-current; the pressed button is aria-pressed=\"true\".",
316 "states": {
317 "buttons-that-switch-in-place": "Buttons, for a choice that changes the page in place, like a view. Your script moves aria-pressed to the button pressed."
318 }
319 },
320 "selbox": {
321 "blurb": "The selection checkbox a list row wears in select mode. Its label restates the row's identity."
322 },
323 "shell-sidebar": {
324 "blurb": "The sidebar shell's chrome on a content page: on a phone, a back control to the index."
325 },
326 "shell-topbar": {
327 "blurb": "The topbar shell's own chrome, as markup. The whole page it belongs to is layout-topbar, under Shells."
328 },
329 "side-nav": {
330 "blurb": "A sidebar inside a panel: a column of links beside the panel's content. In a narrow panel the links become a row that scrolls sideways.",
331 "states": {
332 "four-sections-the-first-open": "rst-side takes its width from its parent. In a flex row, give it flex: 1 1 0, or it can shrink to nothing."
333 }
334 },
335 "signin": {
336 "blurb": "The sign-in screen in every state it passes through: asking for an address, coming back, after the link is sent, when something went wrong, and the other ways in.",
337 "states": {
338 "a-passkey": "The fastest way in for someone who has one, and nothing to type. Offer a second way as well: people lose devices, and a passkey that will not work is a locked door.",
339 "after-the-link-is-sent": "Repeat the address back. It is the only chance to notice a typo before waiting for an email that will never come.",
340 "after-the-link-is-sent-in-another-browser": "When this browser did not send the address, the page does not guess it.",
341 "an-address-that-does-not-look-right": "The message belongs to the field, and that is where focus starts.",
342 "asking-for-an-address": "One field and one button. Nothing to remember and nothing to steal.",
343 "coming-back-with-an-email-link": "The address is on the button, so there is nothing to type.",
344 "coming-back-with-keymail": "For someone coming back: the address they used last time is on the button, so one tap sends it. Remembering that address, and checking it again, is your app's job.",
345 "email-and-password": "If you do use passwords, use the same one-way-in layout, and put a way to reset directly under the button rather than hiding it.",
346 "google-apple-and-the-rest": "We give you the buttons, drawn the way each company requires. We do not give you the sign-in itself: you wire that up to whichever provider you use.",
347 "on-the-way-to-keymail": "A holding page while someone is sent on to sign in. Your app moves the page on by itself; the link is there in case it does not.",
348 "when-keymail-could-not-confirm-it": "The button changes to send a link instead, so nobody is stuck.",
349 "when-keymail-was-offered-and-a-link-went-out": "One line says a link was sent this time. It does not guess why."
350 },
351 "warnings": {
352 "email-and-password": {
353 "title": "We do not recommend passwords",
354 "body": "People reuse them, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it."
355 }
356 }
357 },
358 "snippet": {
359 "blurb": "A line of code to copy: the code in a box, and a Copy button that copy.js shows where the browser has a clipboard."
360 },
361 "stat": {
362 "blurb": "One number on a dashboard, with a label and how much it changed.",
363 "states": {
364 "a-shortened-number": "Showing 4.1k instead of 4120? Put the full number in a \u003cdata\u003e tag's value. Screen readers only read what is on screen, so keep anything a person needs in the number itself.",
365 "a-smaller-number": "You only need a label and a number. Everything else is optional.",
366 "the-big-number": "Give one stat in the row rst-stat=\"lead\" to make it big. It is the same component, just larger.",
367 "when-going-down-is-good": "You set the colour, not the component. Here the number went down and that is good news, so the change is green.",
368 "when-going-up-is-bad": "Here the number went up and that is bad news, so the change is red.",
369 "when-you-cannot-say": "Leave the colour out and the change is grey. Do that when you cannot say if it is good or bad."
370 }
371 },
372 "stat-band": {
373 "blurb": "A row of stats across the top of a dashboard.",
374 "states": {
375 "two-to-a-line-on-a-phone": "rst-stats=\"pairs\" puts two stats on a line on a phone. Without it they stack one to a line."
376 }
377 },
378 "status-pill": {
379 "blurb": "A record's status as a tinted pill, colour and label.",
380 "states": {
381 "neutral": "Neutral is the default tone, so rst-tone=\"neutral\" could be left off."
382 }
383 },
384 "tblock": {
385 "blurb": "A bordered card whose body reveals only while its switch is on, via :has(). The switch is authoritative; the reveal is a display convenience."
386 }
387 },
388 "pages": {
389 "idioms": [
390 {
391 "idiom": "box",
392 "title": "Which card is which",
393 "body": "rst-list and rst-card have no padding by design: they hold a run of rows, and each row pads itself. Put a form, a paragraph, a strip of links or anything else that is not a row straight into one and it renders flush against the border; the text touching the edge is the tell. The padded card for arbitrary content is rst-box, with its heading as a sibling rst-box-head before it."
394 },
395 {
396 "idiom": "list-grid",
397 "title": "Screens stack vertically",
398 "body": "A screen is a column: page-header, then section-header + card, then the next section-header + card, in reading order. Horizontal arrangement is reserved for the idioms that ship it: rst-box-head, rst-field-row, rst-lbar, rst-lrow cells, rst-seg-tabs."
399 }
400 ]
401 }
402}
403