@orkestrel/scaffold 0.0.64 → 0.0.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,9 @@
3
3
  Pick an affordance from what the person is asked for, not from the name a schema gives the field.
4
4
  Where one category draws several ways, let the density and the list size decide.
5
5
 
6
+ Each **Default** and **Alternate** names the styling rung it sits on. Take the rung names and their
7
+ order from [SKILL.md](../SKILL.md) → The styling ladder.
8
+
6
9
  Read [The fixed state set](#the-fixed-state-set) before the catalog: every affordance handles that
7
10
  same set, and each category names only what it adds or changes. Take the data states a whole surface
8
11
  ships — ideal, empty, loading, partial, error — from
@@ -34,8 +37,8 @@ value is a set.
34
37
  `spinner-border spinner-border-sm` in the control's own chrome. Leave the control operable unless
35
38
  its value depends on the work.
36
39
  - **required** — state the requirement in the visible label and set the `required` attribute on the
37
- control. A `text-danger` asterisk is decoration and carries `aria-hidden="true"`; the word in the
38
- label is what a screen reader user gets.
40
+ control. An optional decorative asterisk inherits the label color and carries `aria-hidden="true"`;
41
+ keep the requirement in words rather than relying on the mark or a danger tint.
39
42
  - **with help** — a `.form-text` under the control, wired with `aria-describedby` beside the error
40
43
  message rather than in place of it.
41
44
  - **empty** — the set holds nothing. Say what an entry would be, not "nothing here".
@@ -44,15 +47,27 @@ value is a set.
44
47
 
45
48
  ## Rules that cross every category
46
49
 
50
+ - **Start with one reading column.** Expand related fields only when their labels, errors, and
51
+ controls fit the actual container. Keep help/errors in natural flow and submit/cancel reachable
52
+ at 320 CSS px and enlarged text; take the layout contract from [responsive-layout.md](responsive-layout.md).
53
+ - **Preserve input behavior on touch.** Use the correct `type`, `inputmode`, and `autocomplete`;
54
+ never disable zoom to keep a form still. Enlarge the effective label hit area for checkboxes and
55
+ switches without shrinking the text. Emulated viewport tests do not prove soft-keyboard behavior.
56
+
47
57
  - **Keep a read-only field on the same affordance the edit state uses.** Take `readonly`, or
48
- `disabled` plus a carrier, and neutralize the chrome with one transparent combination declared
49
- once by name — the combination is a class contract, so declare it and reuse it rather than
50
- retyping the utilities. Never swap to `form-control-plaintext`: it drops the horizontal padding,
58
+ `disabled` plus a carrier, and neutralize the chrome with one transparent combination the project
59
+ declares by name in its own stylesheet and token layer. The project chooses that combination; this
60
+ skill prescribes none. Reuse the declared name rather than retyping the utilities at each site.
61
+ Never swap to `form-control-plaintext`: it drops the horizontal padding,
51
62
  so the read view and the edit view reflow against each other.
52
63
  - **Give a locked select `disabled` and a hidden input beside it.** A native select cannot be
53
64
  read-only, so `disabled` stops its value submitting and the hidden input carries that value.
54
- - **Give a chosen filter an accent tone class, not the neutral outline.** A `btn-outline-secondary`
55
- label reads as chosen in light and as muted in dark, so one markup says opposite things.
65
+ - **Verify a chosen filter in every declared theme.** Keep its native checked/pressed state and a
66
+ distinguishable visual treatment; take selection rules from [components.md](components.md) →
67
+ Selection fills. Do not prescribe an accent color as a substitute for that check.
68
+ - **Keep native control foregrounds and states.** For quiet chip or group backgrounds, inherit
69
+ ordinary text and follow [color-modes.md](color-modes.md). Do not neutralize validation feedback
70
+ or recolor every label to match its background.
56
71
  - **Give each field one visible label**, per [bootstrap-reference.md](bootstrap-reference.md) →
57
72
  Forms in production. Take labels, validation timing, and the error summary from that section, and
58
73
  the affordance that carries them from [The catalog](#the-catalog).
@@ -67,7 +82,9 @@ value is a set.
67
82
 
68
83
  ### One line of text
69
84
 
70
- **Default.** An `input.form-control` under its own `label.form-label`, at rung 1.
85
+ **Default.** An `input.form-control` under its own `label.form-label`, at the component rung.
86
+
87
+ The stock control border is `var(--bs-border-color)` — 1.3:1 on the light body and 1.9:1 on the dark one — so it is decorative; the label, the field's fill against the page, and the focus ring carry recognition. Where the boundary is the only cue (a field on a same-color surface), set `--bs-border-color: var(--bs-secondary-color)` on the form scope — adaptive, about 5.6:1 light and 7:1 dark — and re-measure; never a fixed `border-dark`.
71
88
 
72
89
  ```html
73
90
  <label for="account-name" class="form-label">Account name</label>
@@ -86,7 +103,7 @@ declared transparent combination rather than dropping the control.
86
103
  ### Text over many lines
87
104
 
88
105
  **Default.** A `textarea.form-control` with a `rows` attribute sized to the expected answer, at
89
- rung 1.
106
+ the component rung.
90
107
 
91
108
  ```html
92
109
  <label for="incident-notes" class="form-label">Notes</label>
@@ -103,7 +120,7 @@ count in the `.form-text`, and keep it out of a live region unless the cap is cl
103
120
 
104
121
  ### A secret
105
122
 
106
- **Default.** An `input[type=password].form-control` under a visible label, at rung 1.
123
+ **Default.** An `input[type=password].form-control` under a visible label, at the component rung.
107
124
 
108
125
  ```html
109
126
  <label for="passphrase" class="form-label">Passphrase</label>
@@ -122,8 +139,8 @@ one-time code the person must read back.
122
139
 
123
140
  ### A number
124
141
 
125
- **Default.** An `input[type=number].form-control`, at rung 1. In a column of figures add
126
- `text-end font-monospace` so the digits align, at rung 2.
142
+ **Default.** An `input[type=number].form-control`, at the component rung. In a column of figures add
143
+ `text-end font-monospace` so the digits align, at the utility rung.
127
144
 
128
145
  ```html
129
146
  <label for="unit-count" class="form-label">Units</label>
@@ -138,7 +155,7 @@ than a quantity, take the one-line-of-text category instead.
138
155
 
139
156
  ### A number in a bounded range
140
157
 
141
- **Default.** An `input.form-range`, at rung 1, and only when a minimum, a maximum, and a step are all
158
+ **Default.** An `input.form-range`, at the component rung, and only when a minimum, a maximum, and a step are all
142
159
  fixed.
143
160
 
144
161
  ```html
@@ -157,7 +174,7 @@ hand-roll: Bootstrap ships one thumb per input.
157
174
 
158
175
  ### A date
159
176
 
160
- **Default.** An `input[type=date].form-control`, at rung 1. Take the calendar, the keyboard model,
177
+ **Default.** An `input[type=date].form-control`, at the component rung. Take the calendar, the keyboard model,
161
178
  and the locale format from the platform rather than authoring any of them.
162
179
 
163
180
  ```html
@@ -173,7 +190,7 @@ help text, because a native picker takes no per-day exclusion.
173
190
 
174
191
  ### A time
175
192
 
176
- **Default.** An `input[type=time].form-control`, at rung 1, with `step` set to the granularity the
193
+ **Default.** An `input[type=time].form-control`, at the component rung, with `step` set to the granularity the
177
194
  value actually carries.
178
195
 
179
196
  ```html
@@ -189,7 +206,7 @@ select is the lighter control.
189
206
 
190
207
  ### A date and time
191
208
 
192
- **Default.** An `input[type=datetime-local].form-control`, at rung 1.
209
+ **Default.** An `input[type=datetime-local].form-control`, at the component rung.
193
210
 
194
211
  ```html
195
212
  <label for="window-opens" class="form-label">Window opens</label>
@@ -204,7 +221,7 @@ stored in; a local datetime carries none.
204
221
 
205
222
  ### A color
206
223
 
207
- **Default.** An `input.form-control-color[type=color]`, at rung 1.
224
+ **Default.** An `input.form-control-color[type=color]`, at the component rung.
208
225
 
209
226
  ```html
210
227
  <label for="brand-tint" class="form-label">Brand tint</label>
@@ -212,7 +229,8 @@ stored in; a local datetime carries none.
212
229
  ```
213
230
 
214
231
  **Alternates.** Pair the swatch with a text field when the value is copied, pasted, or read aloud
215
- between people. Keep the swatch at a 24×24px target or larger.
232
+ between people. Hold the swatch to the target floor in
233
+ [bootstrap-reference.md](bootstrap-reference.md) → WCAG 2.2 requirements for app UI.
216
234
 
217
235
  **States.** The fixed set. A color input has no empty value, so give the field a default and say what
218
236
  it is.
@@ -220,7 +238,7 @@ it is.
220
238
  ### One on/off answer
221
239
 
222
240
  **Default.** A `.form-check` holding one `input.form-check-input[type=checkbox]` and its
223
- `label.form-check-label`, at rung 1.
241
+ `label.form-check-label`, at the component rung.
224
242
 
225
243
  ```html
226
244
  <div class="form-check">
@@ -239,7 +257,7 @@ flight, and reverts visibly when it fails.
239
257
 
240
258
  ### One of a few
241
259
 
242
- **Default.** A radio group: `fieldset` and `legend` around `.form-check` rows, at rung 1.
260
+ **Default.** A radio group: `fieldset` and `legend` around `.form-check` rows, at the component rung.
243
261
 
244
262
  ```html
245
263
  <fieldset>
@@ -255,18 +273,18 @@ flight, and reverts visibly when it fails.
255
273
  </fieldset>
256
274
  ```
257
275
 
258
- **Alternates.** Take a segmented `.btn-group` of `.btn-check` radios, at rung 2, when the choice
276
+ **Alternates.** Take a segmented `.btn-group` of `.btn-check` radios, at the utility rung, when the choice
259
277
  sits in a toolbar or a filter bar and every option fits on one row without wrapping; give the group
260
278
  `role="radiogroup"` and one accessible name. A radio group and a segmented group draw the same
261
- question, and the list size decides between them. Give a chosen filter an accent tone class rather
262
- than `btn-outline-secondary`.
279
+ question, and the list size decides between them. Verify the chosen-state contract rather than
280
+ requiring either a neutral or an accent hue.
263
281
 
264
282
  **States.** The fixed set, applied to the group rather than to one option. Mark the group invalid,
265
283
  name it in the message, and keep the error under the last row.
266
284
 
267
285
  ### One of many
268
286
 
269
- **Default.** A `select.form-select`, at rung 1.
287
+ **Default.** A `select.form-select`, at the component rung.
270
288
 
271
289
  ```html
272
290
  <label for="territory" class="form-label">Territory</label>
@@ -286,7 +304,7 @@ hidden input. A select whose options are loading is `busy`.
286
304
 
287
305
  ### One of many with an unlisted value admitted
288
306
 
289
- **Default.** An `input.form-control` bound to a `<datalist>`, at rung 1. The list suggests; the
307
+ **Default.** An `input.form-control` bound to a `<datalist>`, at the component rung. The list suggests; the
290
308
  person can still submit a value it does not hold.
291
309
 
292
310
  ```html
@@ -305,7 +323,7 @@ re-target every test and journey that finds this field by role.
305
323
 
306
324
  ### Any of a few
307
325
 
308
- **Default.** `fieldset` and `legend` around `.form-check` checkbox rows sharing one name, at rung 1.
326
+ **Default.** `fieldset` and `legend` around `.form-check` checkbox rows sharing one name, at the component rung.
309
327
 
310
328
  ```html
311
329
  <fieldset>
@@ -327,7 +345,7 @@ text and validate it on the group.
327
345
 
328
346
  ### Any of many
329
347
 
330
- **Default.** A bounded, scrollable list of `.form-check` rows inside a bordered box, at rung 2, with
348
+ **Default.** A bounded, scrollable list of `.form-check` rows inside a bordered box, at the utility rung, with
331
349
  a filter field preceding it so the person can narrow the list before choosing.
332
350
 
333
351
  ```html
@@ -351,7 +369,7 @@ one-action way to clear it.
351
369
 
352
370
  ### A value picked from a searched list
353
371
 
354
- **Default.** A combobox composed from shipped classes at rung 2, with the keyboard model
372
+ **Default.** A combobox composed from shipped classes at the utility rung, with the keyboard model
355
373
  hand-rolled against the APG combobox pattern: an `input.form-control` carrying `role="combobox"`,
356
374
  `aria-expanded`, `aria-controls`, `aria-autocomplete="list"`, and `aria-activedescendant`, over a
357
375
  `ul.dropdown-menu[role=listbox]` of `.dropdown-item` buttons.
@@ -384,7 +402,7 @@ the input stays operable throughout.
384
402
 
385
403
  ### Files
386
404
 
387
- **Default.** An `input[type=file].form-control`, at rung 1.
405
+ **Default.** An `input[type=file].form-control`, at the component rung.
388
406
 
389
407
  ```html
390
408
  <label for="statement" class="form-label">Statement</label>
@@ -402,8 +420,9 @@ toast.
402
420
 
403
421
  ### An ordered set of tags
404
422
 
405
- **Default.** Bootstrap ships no tags input. Compose one at rung 2 from a text field that commits on
406
- Enter plus a row of chips, each chip a `.badge` carrying a `btn-close` with its own accessible name.
423
+ **Default.** Bootstrap ships no tags input. Compose one at the utility rung from a text field that commits on
424
+ Enter plus a row of quiet chips, each carrying a `btn-close` with its own accessible name.
425
+ Use a utility-composed span so text inherits and the close control retains its normal font size.
407
426
 
408
427
  ```html
409
428
  <label for="tag-entry" class="form-label">Tags</label>
@@ -411,19 +430,19 @@ Enter plus a row of chips, each chip a `.badge` carrying a `btn-close` with its
411
430
  <div id="tag-entry-help" class="form-text">Press Enter to add a tag.</div>
412
431
  <ul class="list-unstyled d-flex flex-wrap gap-2 mt-2">
413
432
  <li>
414
- <span class="badge text-bg-secondary d-inline-flex align-items-center gap-1">
433
+ <span class="d-inline-flex align-items-center gap-2 bg-secondary-subtle rounded-pill px-2 py-1">
415
434
  Priority
416
- <button
417
- type="button"
418
- class="btn-close"
419
- data-bs-theme="dark"
420
- aria-label="Remove Priority"
421
- ></button>
435
+ <button type="button" class="btn-close" aria-label="Remove Priority"></button>
422
436
  </span>
423
437
  </li>
424
438
  </ul>
425
439
  ```
426
440
 
441
+ Keep tag text inherited and let the close icon follow the active mode. Measure the close button
442
+ against the chip and its hit area against the target floor in
443
+ [bootstrap-reference.md](bootstrap-reference.md) → WCAG 2.2 requirements for app UI. Take a retained `.badge`
444
+ through [color-modes.md](color-modes.md) → Badges and removable tags.
445
+
427
446
  **Alternates.** Where the tags come from a fixed vocabulary, this is the any-of-many category and the
428
447
  list is the better control. Where order carries meaning, give the reorder a non-drag path — a move
429
448
  control per chip.
@@ -433,10 +452,10 @@ control per chip.
433
452
 
434
453
  ### A rating
435
454
 
436
- **Default.** Bootstrap ships no rating. Draw the interactive form as a radio group at rung 2 — one
455
+ **Default.** Bootstrap ships no rating. Draw the interactive form as a radio group at the utility rung — one
437
456
  radio per value, restyled through `.btn-check` — so the keyboard model, the name, and the submitted
438
457
  value come from the platform. Star chrome over that structure is an authored class contract at
439
- rung 4.
458
+ the authored rung.
440
459
 
441
460
  ```html
442
461
  <fieldset>
@@ -456,7 +475,7 @@ per [components.md](components.md) → Status glyph marks; it is not a control.
456
475
 
457
476
  ### A step in a sequence
458
477
 
459
- **Default.** Draw a step indicator from shipped parts at rung 1 — a `nav` or `.list-group-numbered`
478
+ **Default.** Draw a step indicator from shipped parts at the component rung — a `nav` or `.list-group-numbered`
460
479
  whose current item carries `aria-current="step"`, with a `.progress` bar over a long sequence. A step
461
480
  indicator reports where the person is and holds no value, so it is not a field.
462
481