@orkestrel/scaffold 0.0.35 → 0.0.36
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.
- package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +146 -194
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +29 -22
- package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +31 -4
- package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +103 -33
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +1 -1
- package/dist/host/agents/skills/orkestrel-human-journey/SKILL.md +124 -0
- package/dist/host/agents/skills/orkestrel-human-journey/agents/openai.yaml +4 -0
- package/dist/host/agents/skills/orkestrel-human-journey/references/captures.md +74 -0
- package/dist/host/agents/skills/orkestrel-human-journey/references/layer.md +131 -0
- package/dist/host/claude/rules/documentation.md +4 -1
- package/dist/host/claude/skills/orkestrel-human-journey/SKILL.md +10 -0
- package/dist/host/claude/skills/orkestrel-polish-surface/SKILL.md +1 -1
- package/dist/host/manifest.json +30 -1
- package/dist/host/tests/policy.test.ts +33 -0
- package/dist/host/tests/setupPolicy.ts +319 -2
- package/dist/src/core/index.cjs +1 -1
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.js +1 -1
- package/dist/src/core/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -14,101 +14,99 @@ description: >-
|
|
|
14
14
|
|
|
15
15
|
# Enterprise Bootstrap
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Set a deliberate visual direction, build it from Bootstrap 5.3 components and utilities, and settle
|
|
18
|
+
every claim about the result from what renders.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
Open the reference that owns a subject before writing markup. Never guess a class name: an invented
|
|
21
|
+
utility (`.vw-50`, `.pointer-events-none`) has no rule in the shipped CSS and fails silently. Pick
|
|
22
|
+
components from [components.md](references/components.md) → Choosing components, take their markup
|
|
23
|
+
from the same file, and take fine layout from [utilities.md](references/utilities.md). Where
|
|
24
|
+
Bootstrap ships no component for the need — combobox, date picker, tags input, data grid, tree —
|
|
25
|
+
work the native-first ladder in [bootstrap-reference.md](references/bootstrap-reference.md) → When
|
|
26
|
+
not to hand-roll before building one.
|
|
20
27
|
|
|
21
28
|
| Layer | File | Holds |
|
|
22
29
|
| -------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
23
|
-
| Operate | `SKILL.md` (this file) | Process, decision rules,
|
|
24
|
-
| Design craft | [frontend-design.md](references/frontend-design.md) |
|
|
30
|
+
| Operate | `SKILL.md` (this file) | Process, decision rules, checklist |
|
|
31
|
+
| Design craft | [frontend-design.md](references/frontend-design.md) | Aesthetic, typography, signature, interface copy, anti-defaults |
|
|
25
32
|
| Components | [components.md](references/components.md) | Bootstrap component markup + enterprise selection notes |
|
|
26
33
|
| Utilities | [utilities.md](references/utilities.md) | Class index, helpers, composition notes |
|
|
27
34
|
| Bootstrap deep | [bootstrap-reference.md](references/bootstrap-reference.md) | Color modes, theming/tokens, forms, JS lifecycle, a11y depth, enterprise patterns |
|
|
28
35
|
|
|
29
|
-
Open the relevant reference instead of guessing class names or watering down the craft — the class indexes exist because invented utilities (`.vw-50`, `.pointer-events-none`) ship silent no-ops.
|
|
30
|
-
|
|
31
36
|
---
|
|
32
37
|
|
|
33
|
-
## Portability
|
|
34
|
-
|
|
35
|
-
Product-agnostic. Apply the same way wherever this package is loaded.
|
|
38
|
+
## Portability
|
|
36
39
|
|
|
37
|
-
1. **
|
|
38
|
-
2. **
|
|
39
|
-
3. **
|
|
40
|
-
4. **
|
|
41
|
-
5. **
|
|
42
|
-
6. **
|
|
43
|
-
7. **
|
|
40
|
+
1. **Assume no stack.** Infer it from the workspace. Do not assume Vue, React, a skin library, a folder layout, or a named product.
|
|
41
|
+
2. **Target Bootstrap 5.3.x** class names and behaviors. Hold a compatible skin that keeps `.btn`, `.card`, `.form-control`, and `data-bs-*` to the same contracts.
|
|
42
|
+
3. **Follow the project's code law.** Its `AGENTS.md`, lint rules, and design system decide language, layout, and forbidden patterns. This package owns UI craft and Bootstrap usage, not language law.
|
|
43
|
+
4. **Write framework-neutral markup** — semantic HTML plus Bootstrap classes. Wire behavior with what the project already uses; in an SPA prefer the framework-native Bootstrap wrappers over raw `bootstrap.*` JS ([bootstrap-reference.md](references/bootstrap-reference.md) → JavaScript lifecycle).
|
|
44
|
+
5. **Keep this folder intact** so the relative links between its files resolve. Install or vendor it wherever the tooling looks for skills; the paths are tooling-specific, the content is not.
|
|
45
|
+
6. **Use the project's installed Bootstrap** when it has one; otherwise take the CDN snippet from [bootstrap-reference.md](references/bootstrap-reference.md) → Quick start (5.3.8).
|
|
46
|
+
7. **Apply this package** to UI, Bootstrap, and visual-design work matching the description above. When the user points at it, treat it as authoritative for the visual pass.
|
|
44
47
|
|
|
45
48
|
---
|
|
46
49
|
|
|
47
50
|
## Dual mandate
|
|
48
51
|
|
|
49
|
-
1. **Design
|
|
50
|
-
2. **Bootstrap
|
|
52
|
+
1. **Design direction** — take a point of view rooted in the _subject_ (audience, job-to-be-done, vernacular). Take one justified aesthetic risk, in one place.
|
|
53
|
+
2. **Bootstrap execution** — components and utilities first; custom CSS only when the system cannot express the need; paint through `--bs-*` so light and dark both survive.
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
Match the density to the context: a marketing page may open with a thesis-hero, an authenticated
|
|
56
|
+
tool opens with clarity and scan paths. In product UI put the signature in the chrome, never in the
|
|
57
|
+
data ([frontend-design.md](references/frontend-design.md) → Where the signature lives).
|
|
53
58
|
|
|
54
59
|
---
|
|
55
60
|
|
|
56
61
|
## Process
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
2. **Plan** — token system: **color** (4–6 named values), **type** (display / body / utility), **layout** (prose + ASCII if useful), **signature** (one memorable element).
|
|
62
|
-
3. **Critique the plan** — if swapping the logo would make it "any SaaS," revise. Avoid clustered AI defaults unless the brief asks: cream+#F4F1EA+serif+terracotta; near-black+acid green/vermilion; broadsheet hairlines / zero radius / dense columns. Brief wins when it pins a direction.
|
|
63
|
-
4. **Build** — Bootstrap from components/utilities; map plan tokens onto theme variables or a thin skin — no scattered one-off hex (token discipline: [bootstrap-reference.md](references/bootstrap-reference.md) → Theming & design tokens). Watch selector specificity (utility vs custom canceling).
|
|
64
|
-
5. **Critique again** — remove one accessory (Chanel). Contrast, focus, `prefers-reduced-motion`, mobile, all five states present. Critique the render, not the markup.
|
|
63
|
+
Design craft — subject grounding, hero and thesis, typography, structure, motion, restraint, and
|
|
64
|
+
interface copy — lives in [frontend-design.md](references/frontend-design.md). Read it before
|
|
65
|
+
setting a direction. The loop:
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
1. **Ground** — name the subject, the audience, and the screen's single job, and state them. Use known user preferences and prior designs as hints, not templates.
|
|
68
|
+
2. **Plan** — build a token system: **color** (4–6 named values), **type** (display / body / utility), **layout** (prose plus ASCII if useful), **signature** (one memorable element).
|
|
69
|
+
3. **Critique the plan** — if swapping the logo would make it "any SaaS", revise. Avoid the clustered AI defaults unless the brief asks for them: cream + #F4F1EA + serif + terracotta; near-black + acid green or vermilion; broadsheet hairlines, zero radius, dense columns. The brief wins when it pins a direction.
|
|
70
|
+
4. **Build** — compose Bootstrap components and utilities; map the plan's tokens onto theme variables or a thin skin, with no scattered one-off hex ([bootstrap-reference.md](references/bootstrap-reference.md) → Theming & design tokens). Watch selector specificity: a utility and a custom rule that cancel each other show up as padding and margin bugs.
|
|
71
|
+
5. **Critique the render** — remove one accessory. Check contrast, focus, `prefers-reduced-motion`, mobile, and all five states. Critique what rendered, not the markup.
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
---
|
|
73
|
+
Brainstorm privately; show a direction only once it satisfies the brief and the quality floor
|
|
74
|
+
([frontend-design.md](references/frontend-design.md) → Process).
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
**Rendered proof.** Settle every claim about a screen from a capture, never from source alone;
|
|
77
|
+
`.agents/orchestration.md` owns this law where it is present. The review input here is captures at
|
|
78
|
+
both viewports and both themes plus an accessibility snapshot; source only corroborates the
|
|
79
|
+
mechanism. For a full review-round campaign built on that evidence, use the
|
|
80
|
+
`orkestrel-polish-surface` skill instead of improvising one here.
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
- **Structure is information** — eyebrows, `01/02/03`, dividers only when order/taxonomy is real.
|
|
78
|
-
- **Typography carries personality** — deliberate pairing and scale; characterful display, restrained.
|
|
79
|
-
- **Motion with purpose** — Bootstrap transitions first; one orchestrated moment if earned; less motion often beats "AI sparkle."
|
|
80
|
-
- **Match complexity to vision** — maximalist = elaborate execution; minimal = precision.
|
|
81
|
-
- **Copy is design** — see Writing below and frontend-design.
|
|
82
|
+
**Mechanical proof.** Three instruments settle what a capture cannot. Pair each one with a negative
|
|
83
|
+
control drawn from outside the population it covers, and treat an instrument whose control passes as
|
|
84
|
+
broken; `.claude/rules/quality.md` owns this law where it is present:
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
- Active voice; controls say what happens ("Save changes," not "Submit").
|
|
87
|
-
- Same verb across button → toast → confirm ("Publish" / "Published").
|
|
88
|
-
- Errors: what failed + how to fix; no vague apologies.
|
|
89
|
-
- Empty states: invite the next action.
|
|
90
|
-
- Plain verbs, sentence case, no filler; one job per element (label vs example).
|
|
91
|
-
- Where the surrounding context already names the object, the visible label is one word and the full phrase rides in `aria-label` — the button reads "Stop", the accessible name says what it stops.
|
|
92
|
-
- One glyph means one thing across the whole surface: a check that means "finished" here must not mean "selected" there.
|
|
86
|
+
- **Contrast, composited.** Read every pairing through a reader that composites the painted layers, in both themes ([bootstrap-reference.md](references/bootstrap-reference.md) → Measuring the bars).
|
|
87
|
+
- **Authored classes against the shipped cascade.** Extract every class authored in the templates and components, and fail the run on one that has no rule in the compiled CSS the page loads. Assert a population floor so an extractor that quietly matched nothing cannot pass, and control it with a class you know is absent.
|
|
88
|
+
- **One glyph, one meaning.** Register each status glyph against the meaning it carries. No meaning takes two glyphs, no glyph serves two meanings, and every registered glyph resolves in the icon set actually shipped.
|
|
93
89
|
|
|
94
90
|
---
|
|
95
91
|
|
|
96
92
|
## Bootstrap operating principles
|
|
97
93
|
|
|
98
|
-
1. **Mobile first** — smallest screen first, then `sm` / `md` / `lg` / `xl` / `xxl
|
|
99
|
-
2. **Semantic HTML** — `nav`, `main`, `section`, heading order
|
|
100
|
-
3. **Work down the styling ladder below** — component classes, then utilities, then Bootstrap's own extension points
|
|
101
|
-
4. **Test
|
|
102
|
-
5. **
|
|
94
|
+
1. **Mobile first** — smallest screen first, then `sm` / `md` / `lg` / `xl` / `xxl`.
|
|
95
|
+
2. **Semantic HTML** — `nav`, `main`, `section`, heading order.
|
|
96
|
+
3. **Work down the styling ladder below** — component classes, then utilities, then Bootstrap's own extension points.
|
|
97
|
+
4. **Test every breakpoint you claim.**
|
|
98
|
+
5. **Reach for Bootstrap's own transitions before writing custom animation**, spend one orchestrated moment at most, and wrap any custom animation in `prefers-reduced-motion: no-preference` ([bootstrap-reference.md](references/bootstrap-reference.md) → Reduced motion).
|
|
99
|
+
6. **Resolve every treatment in the shipped cascade** — Bootstrap plus every skin and dependency stylesheet the page pulls in — never from docs memory. A class with no rule of its own may still inherit one, and a token pair that passes in stock Bootstrap can fail under a compatible skin. Measure the `*-subtle` / `*-emphasis` recipes too, once per theme, with a reader that composites the translucent layers ([bootstrap-reference.md](references/bootstrap-reference.md) → Measuring the bars).
|
|
103
100
|
|
|
104
101
|
### The styling ladder
|
|
105
102
|
|
|
106
|
-
Work down these rungs in order. Reach a lower rung only when the one above genuinely cannot express
|
|
103
|
+
Work down these rungs in order. Reach a lower rung only when the one above genuinely cannot express
|
|
104
|
+
the need.
|
|
107
105
|
|
|
108
|
-
1. **The component's own classes, in its documented structure.**
|
|
109
|
-
2. **Bootstrap utilities, for refinement.** Spacing, flex, display, sizing, text, borders,
|
|
106
|
+
1. **The component's own classes, in its documented structure.** Use the right elements, nesting, class names, and required ARIA: a card is `.card` wrapping `.card-body` wrapping `.card-title`, not a `div` with borrowed padding. Variants, states, color modes, and responsive behavior all hang off that structure.
|
|
107
|
+
2. **Bootstrap utilities, for refinement.** Spacing, flex, display, sizing, text, borders, color. Compose utilities rather than reaching past them, and use only classes that exist in [utilities.md](references/utilities.md).
|
|
110
108
|
3. **Bootstrap's own extension points.** Component `--bs-{component}-*` variables and the utilities API, when a real gap remains after rungs 1 and 2.
|
|
111
|
-
4. **Anything beyond Bootstrap's conventions is the developer's call, not yours.** Stop at rung 3
|
|
109
|
+
4. **Anything beyond Bootstrap's conventions is the developer's call, not yours.** Stop at rung 3 and say plainly what rung 4 would require.
|
|
112
110
|
|
|
113
111
|
Never open at rung 4. Specifically, do not reach first for:
|
|
114
112
|
|
|
@@ -116,138 +114,100 @@ Never open at rung 4. Specifically, do not reach first for:
|
|
|
116
114
|
- a `<style>` block in a page or component;
|
|
117
115
|
- a new stylesheet rule for something a utility already does.
|
|
118
116
|
|
|
119
|
-
Each
|
|
117
|
+
Each ends the cascade for that element: it outranks the utilities, it ignores `--bs-*` retheming, and
|
|
118
|
+
it does not change across breakpoints or color modes.
|
|
120
119
|
|
|
121
120
|
### Hierarchy & actions
|
|
122
121
|
|
|
123
|
-
| Intent | Typical choice
|
|
124
|
-
| ----------- |
|
|
125
|
-
| Primary | `btn btn-primary` — **one** clear primary per region
|
|
126
|
-
| Secondary | `btn-
|
|
127
|
-
| Destructive | `btn-danger` + the confirmation ladder
|
|
128
|
-
| Tertiary | `btn-link` or text links
|
|
129
|
-
| Status | `badge` / `alert` / `*-emphasis` — **
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
| Intent | Typical choice |
|
|
123
|
+
| ----------- | ------------------------------------------------------------------- |
|
|
124
|
+
| Primary | `btn btn-primary` — **one** clear primary per region |
|
|
125
|
+
| Secondary | `btn-secondary` — solid, so the surface underneath cannot change it |
|
|
126
|
+
| Destructive | `btn-danger` + the confirmation ladder |
|
|
127
|
+
| Tertiary | `btn-link` or text links |
|
|
128
|
+
| Status | `badge` / `alert` / `*-emphasis` — **icon + color + word** |
|
|
129
|
+
|
|
130
|
+
**Outline buttons are the decorative tier.** They paint no background of their own, so they borrow
|
|
131
|
+
whatever surface they sit on and their contrast is surface- and theme-dependent by construction:
|
|
132
|
+
against stock Bootstrap the whole `btn-outline-*` family misses 4.5:1 across the dark theme and on
|
|
133
|
+
light tinted surfaces — cards, subtle alerts. Give any action that carries information or
|
|
134
|
+
consequence the solid variant. Solid variants paint their own background and measure identically on
|
|
135
|
+
every surface, and the stock fills sit at the 4.5:1 bar with nothing to spare. Re-measure a solid
|
|
136
|
+
variant whenever anything layers over it — an `opacity-*` utility, a translucent overlay, a skin's
|
|
137
|
+
own tint.
|
|
138
|
+
|
|
139
|
+
A status mark with **no text** is an icon glyph, never a `badge`
|
|
140
|
+
([components.md](references/components.md) → Badge).
|
|
132
141
|
|
|
133
142
|
### Surfaces, color, contrast
|
|
134
143
|
|
|
135
|
-
- **Contrast bars, measured in both themes:** **≥ 4.5:1** for anything information-bearing — `small`, captions, and meta text included — and **≥ 3:1** for textless marks, state indicators, and the hover/focus chrome that carries state. Verify
|
|
136
|
-
- `text-
|
|
144
|
+
- **Contrast bars, measured in both themes:** **≥ 4.5:1** for anything information-bearing — `small`, captions, and meta text included — and **≥ 3:1** for textless marks, state indicators, and the hover/focus chrome that carries state. Verify Bootstrap's own palette too; the docs admit some defaults fall short. Read both themes — a pairing that passes light routinely fails dark.
|
|
145
|
+
- **Information-bearing status text takes the `-emphasis` pair.** Plain `text-success` and `text-danger` miss the bar across the dark theme and on light tinted surfaces, and `text-warning` is theme-asymmetric — unreadable on light, comfortable on dark. Never make a plain semantic color the encoding; use it only as decoration beside an encoding that already passes.
|
|
146
|
+
- `text-body-tertiary` carries no information anywhere: it measures under 4.5:1 on every surface in both themes. Tier text a user must read `text-body-secondary` or better, and keep tertiary for genuinely decorative marks.
|
|
147
|
+
- **A subtle fill degrades everything inside it one notch.** Inside `alert-*` and the `*-subtle` backgrounds, outline buttons and plain semantic text fail even in light — so information-bearing text there is `-emphasis` and every button is solid.
|
|
148
|
+
- **A primary fill destroys every semantic color.** On `.active`, `.bg-primary`, and `text-bg-*` surfaces every tone class measured lands under the bar in both themes, the `-emphasis` family included, because the fill supplies its own contrast color and the tone class overrides it with one tuned for a different background. Carry no tone class inside such a fill; let the surface's contrast color take the text, keep the status encoded by icon and word, and verify by capturing the selected state ([components.md](references/components.md) → Selection fills).
|
|
137
149
|
- Disabled controls are exempt from the bars, but a disabled **destructive** control must not keep full danger saturation — at full strength it still reads as armed. Neutralize the variant while it is disabled and carry the reason on the control with `aria-describedby` (plus `title` for pointer users), never `title` alone.
|
|
138
|
-
- Prefer `bg-body`, `bg-body-secondary`, `bg-body-tertiary` over raw `bg-white
|
|
139
|
-
- Pairings: `text-bg-*`, `*-subtle`, `*-emphasis`, `text-body` / `text-body-secondary`.
|
|
140
|
-
- On **dark surfaces**, scope `data-bs-theme="dark"` rather than
|
|
141
|
-
- Support `data-bs-theme="light"` and `dark` when the product offers both
|
|
150
|
+
- Prefer `bg-body`, `bg-body-secondary`, `bg-body-tertiary` over raw `bg-white` / `bg-light`, and drive custom paint from `var(--bs-…)` — they track `data-bs-theme`, a hard-coded hex does not.
|
|
151
|
+
- Pairings: `text-bg-*`, `*-subtle`, `*-emphasis`, `text-body` / `text-body-secondary`. `text-muted` is deprecated — use `text-body-secondary`.
|
|
152
|
+
- On **dark surfaces**, scope `data-bs-theme="dark"` rather than the deprecated component variants `navbar-dark`, `dropdown-menu-dark`, `btn-close-white`, and `carousel-dark`; gray-on-dark outlines often fail contrast.
|
|
153
|
+
- Support `data-bs-theme="light"` and `dark` when the product offers both. Mechanics: [bootstrap-reference.md](references/bootstrap-reference.md) → Color modes.
|
|
142
154
|
|
|
143
155
|
### Density, layout, responsive
|
|
144
156
|
|
|
145
|
-
- Enterprise density: `table-sm`, `btn-sm` / `btn-group-sm`, compact toolbars — but keep every interactive target **≥ 24×24px
|
|
146
|
-
-
|
|
147
|
-
- Cards earn their keep: `.card` when grouping helps; otherwise spacing
|
|
148
|
-
-
|
|
149
|
-
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
- **
|
|
161
|
-
-
|
|
162
|
-
-
|
|
163
|
-
|
|
164
|
-
### Views & navigation
|
|
165
|
-
|
|
166
|
-
- In-page view switching → real **tabs** (`nav-tabs` / `nav-pills` / `nav-underline` + tab panes / equivalent state), not scroll-only hash links dressed as tabs.
|
|
167
|
-
- Active items: `aria-current="page"` or `aria-selected`. Exactly **one** `aria-current` per selection — per nav, per list, per table; two is no selection at all.
|
|
168
|
-
- A mark laid on a selected/active fill must survive it: a `text-bg-*`-family mark on a `.active` fill of the same color is overridden and vanishes, while `text-body-emphasis` keeps reading. Same trap for `btn-check` filter labels ([components.md](references/components.md) → Selection fills).
|
|
169
|
-
- Icon-only controls: `aria-label` (and `title` when helpful).
|
|
170
|
-
- Breadcrumbs only for real hierarchy; command palette only on top of visible nav.
|
|
171
|
-
|
|
172
|
-
### Forms (decision rules)
|
|
173
|
-
|
|
174
|
-
- Visible labels (top-aligned by default) or `.form-floating` — never placeholder-only.
|
|
175
|
-
- Validate on **blur**, re-validate error fields on input, everything on submit; keep submit **enabled**.
|
|
176
|
-
- Long forms pair a focusable error summary with inline `.invalid-feedback` per field (`aria-describedby`, `aria-invalid`).
|
|
177
|
-
- Bootstrap's client-side validation styles aren't exposed to assistive tech — accessible flows use the server-side classes or native validation. Mechanics + canonical JS: [bootstrap-reference.md](references/bootstrap-reference.md) → Forms in production.
|
|
178
|
-
- Units/money: `.input-group` + `.input-group-text` (+ `.has-validation` when feedback present).
|
|
179
|
-
- Multi-step: per-step validation, back without loss, never re-ask collected data.
|
|
180
|
-
|
|
181
|
-
### Component cheat sheet
|
|
182
|
-
|
|
183
|
-
| Problem | Start with |
|
|
184
|
-
| ------------------- | ------------------------------------------------------------------------- |
|
|
185
|
-
| App frame | `navbar` + responsive `offcanvas-{bp}` sidebar + `container-fluid` |
|
|
186
|
-
| Page sections | spaced headings or `card` |
|
|
187
|
-
| Actions | `btn`, `btn-group`, `btn-toolbar`, `d-grid gap-2` |
|
|
188
|
-
| In-page views | `nav-tabs` / `nav-pills` / `nav-underline` + panes |
|
|
189
|
-
| Data | `table` + `table-responsive` (+ patterns in the deep reference) |
|
|
190
|
-
| Filters / secondary | `offcanvas`, `dropdown`, `collapse` |
|
|
191
|
-
| Long help | `accordion` and/or scrollable `modal` |
|
|
192
|
-
| Forms | `form-control`, `form-select`, `form-floating`, `input-group`, validation |
|
|
193
|
-
| Feedback | `alert`, `toast`, `badge`, `progress`, `spinner`, `placeholder` |
|
|
194
|
-
| Steps / schemas | `list-group` / `list-group-numbered` |
|
|
195
|
-
| Value pickers | `form-select` / native inputs — never a dropdown posing as an input |
|
|
196
|
-
|
|
197
|
-
Markup: [components.md](references/components.md). Fine layout: [utilities.md](references/utilities.md).
|
|
157
|
+
- Enterprise density: `table-sm`, `btn-sm` / `btn-group-sm`, compact toolbars — but keep every interactive target **≥ 24×24px**, measured on the rendered box rather than assumed from the class (WCAG 2.2); pad hit areas rather than shrinking them.
|
|
158
|
+
- Where information density is the screen's job, take the `-sm` family across a control row together — `btn-sm` with `form-control-sm`, `form-select-sm`, `input-group-sm` — so the row shares one height. Never mix control sizes within one row.
|
|
159
|
+
- Cards earn their keep: `.card` when grouping helps; otherwise spacing and type.
|
|
160
|
+
- Swap conditional chrome in place. A bulk-action bar or an alert that shoves the toolbar down shifts the layout mid-task.
|
|
161
|
+
- App shell, dense tables, filter bars, and the ranked responsive strategies for wide data: [bootstrap-reference.md](references/bootstrap-reference.md) → Enterprise patterns. Spacing, toolbar, truncation, and print composition: [utilities.md](references/utilities.md) → Composition habits.
|
|
162
|
+
|
|
163
|
+
### States & feedback
|
|
164
|
+
|
|
165
|
+
- **Every data surface ships five states:** ideal, empty, loading, partial, error. It is not done until all five exist. Loading thresholds, empty and error specifics, and the channel matrix for toast / inline alert / banner / modal: [bootstrap-reference.md](references/bootstrap-reference.md) → The five states, Feedback discipline.
|
|
166
|
+
- **Build a blocking decision on the native `<dialog>`.** `showModal()` brings focus containment, Esc, an inert background, and top-layer stacking from the platform, with no instance to construct and none to leak on unmount. Dress it with Bootstrap chrome inside ([components.md](references/components.md) → Modal). Reach for `.modal` and its JS only when the project already drives its dialogs that way.
|
|
167
|
+
- **Destructive actions:** prefer undoable over interrupting. Ladder and confirmation contracts: [bootstrap-reference.md](references/bootstrap-reference.md) → Destructive actions.
|
|
168
|
+
|
|
169
|
+
### Forms
|
|
170
|
+
|
|
171
|
+
- Give every field a visible label (top-aligned by default) or `.form-floating` — never placeholder-only.
|
|
172
|
+
- Validate on **blur**, re-validate error fields on input, re-check everything on submit, and keep submit **enabled**. Never disable submit as a validation strategy.
|
|
173
|
+
- Pair a focusable error summary with inline `.invalid-feedback` per field (`aria-describedby`, `aria-invalid`).
|
|
174
|
+
- Layout, validation mechanics and their assistive-tech limitation, input groups, autosave, and multi-step rules: [bootstrap-reference.md](references/bootstrap-reference.md) → Forms in production, Wizards & multi-step forms.
|
|
198
175
|
|
|
199
176
|
### When custom CSS is justified
|
|
200
177
|
|
|
201
|
-
This is rung 4 of the styling ladder, so it is the developer's decision. Propose it, name what it
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
-
|
|
214
|
-
- Purple-gradient / glow / emoji / pill-soup decoration without subject reason
|
|
215
|
-
- Faux widgets that break focus and theme behavior — a `div` pretending to be a select, grid, or tab
|
|
216
|
-
- Hand-rolling combobox/datepicker/data-grid when native or an accessible library exists ([bootstrap-reference.md](references/bootstrap-reference.md) → When not to hand-roll)
|
|
217
|
-
- Scroll anchors posing as tabs
|
|
218
|
-
- Color-only status
|
|
219
|
-
- An empty `.badge` used as a dot — `.badge:empty { display: none }` means it never rendered
|
|
220
|
-
- Assuming an unfilled `.badge` is transparent — compatible skins may give it a default background; a muted badge states its fill (`bg-*-subtle`, `bg-transparent`)
|
|
221
|
-
- A colored mark dropped onto a selected/active fill of the same family — the fill wins and the mark disappears
|
|
222
|
-
- Disabled destructive controls left at full danger saturation
|
|
223
|
-
- Treatments accepted from docs memory instead of the compiled cascade
|
|
224
|
-
- Layout-shifting conditional chrome (bulk bars or alerts that shove the toolbar — swap in place)
|
|
225
|
-
- Nested bordered divs instead of real `card` structure
|
|
226
|
-
- Hard-coded hex that ignores `data-bs-theme`
|
|
227
|
-
- Deprecated dark variants (`navbar-dark`, `dropdown-menu-dark`, `btn-close-white`, `carousel-dark`, `text-muted`) in new code
|
|
228
|
-
- Inventing utility classes not in [utilities.md](references/utilities.md) — they fail silently
|
|
229
|
-
- Disabling submit as a validation strategy; blocking paste in password/OTP fields
|
|
230
|
-
- Errors delivered as toasts or hover tooltips
|
|
231
|
-
- One infinite stack of pastel cards for documentation (prefer accordion/sections)
|
|
178
|
+
This is rung 4 of the styling ladder, so it is the developer's decision. Propose it, name what it
|
|
179
|
+
buys, and do not take it unprompted. Exhaust rungs 1–3 first: correct component structure, then
|
|
180
|
+
utilities, then the extension points — component `--bs-{component}-*` variables for restyling, the
|
|
181
|
+
utilities API for missing utility steps ([bootstrap-reference.md](references/bootstrap-reference.md)
|
|
182
|
+
→ Theming).
|
|
183
|
+
|
|
184
|
+
When the developer authorizes it:
|
|
185
|
+
|
|
186
|
+
- Name it in Bootstrap vocabulary.
|
|
187
|
+
- Take colors from `var(--bs-…)` and theme tokens so light and dark both work.
|
|
188
|
+
- Use logical properties (`margin-inline-start`, not `margin-left`) so RTL works.
|
|
189
|
+
- Keep the surface area minimal and document why.
|
|
190
|
+
- Write a stylesheet rule, never a `style` attribute or a `<style>` block.
|
|
232
191
|
|
|
233
192
|
---
|
|
234
193
|
|
|
235
194
|
## Accessibility baseline
|
|
236
195
|
|
|
237
|
-
- Skip link to main; landmarks; `h1` → `h2` order
|
|
238
|
-
- `aria-label` on icon-only controls; targets ≥ 24×24px
|
|
239
|
-
- `aria-current` / `aria-selected` on active nav
|
|
240
|
-
- `aria-expanded` / `aria-controls` for disclosure
|
|
241
|
-
- `aria-describedby` for help and errors; `aria-invalid` on failed fields
|
|
242
|
-
- Live regions match the message: an async status mark is `role="status"`; an alert-styled notice is `role="alert"
|
|
243
|
-
- A form whose host already names the request associates with that name (`aria-labelledby`) instead of repeating the prompt as its own label
|
|
244
|
-
- Visible focus
|
|
245
|
-
- Focus not obscured by sticky chrome (`scroll-margin-top`); focus moved deliberately on SPA route change, failed submit, and row delete
|
|
246
|
-
- Meaning
|
|
247
|
-
-
|
|
248
|
-
-
|
|
249
|
-
|
|
250
|
-
WCAG 2.2 deltas, APG pattern contracts, reduced motion, and SPA focus recipes:
|
|
196
|
+
- Skip link to main; landmarks; `h1` → `h2` order.
|
|
197
|
+
- `aria-label` on icon-only controls; targets ≥ 24×24px.
|
|
198
|
+
- `aria-current` / `aria-selected` on active nav and tabs — exactly one `aria-current` per selection.
|
|
199
|
+
- `aria-expanded` / `aria-controls` for disclosure.
|
|
200
|
+
- `aria-describedby` for help and errors; `aria-invalid` on failed fields.
|
|
201
|
+
- Live regions match the message: an async status mark is `role="status"`; an alert-styled notice is `role="alert"`.
|
|
202
|
+
- A form whose host already names the request associates with that name (`aria-labelledby`) instead of repeating the prompt as its own label.
|
|
203
|
+
- Visible focus — keep the Bootstrap rings, use the `.focus-ring` helper for custom elements, and never write `outline: none`.
|
|
204
|
+
- Focus not obscured by sticky chrome (`scroll-margin-top`); focus moved deliberately on SPA route change, failed submit, and row delete.
|
|
205
|
+
- Meaning never by color alone; contrast verified.
|
|
206
|
+
- Every drag interaction has a non-drag alternative.
|
|
207
|
+
- Dialogs carry `aria-labelledby`; let the platform or Bootstrap trap and restore focus rather than scripting it; dispose Bootstrap instances in SPAs on unmount.
|
|
208
|
+
|
|
209
|
+
WCAG 2.2 deltas, APG pattern contracts, reduced motion, and SPA focus recipes:
|
|
210
|
+
[bootstrap-reference.md](references/bootstrap-reference.md) → Accessibility.
|
|
251
211
|
|
|
252
212
|
---
|
|
253
213
|
|
|
@@ -255,31 +215,23 @@ WCAG 2.2 deltas, APG pattern contracts, reduced motion, and SPA focus recipes: [
|
|
|
255
215
|
|
|
256
216
|
```
|
|
257
217
|
Progress:
|
|
258
|
-
- [ ]
|
|
218
|
+
- [ ] Project code law followed; no wrong-stack assumptions
|
|
259
219
|
- [ ] Subject, audience, single job stated
|
|
260
|
-
- [ ] Design plan: palette, type, layout, one signature
|
|
261
|
-
- [ ]
|
|
262
|
-
- [ ] Styling ladder held:
|
|
263
|
-
- [ ] Plan tokens mapped to theme / --bs-* (no hex scatter); light
|
|
264
|
-
- [ ] Copy
|
|
220
|
+
- [ ] Design plan critiqued against the AI defaults: palette, type, layout, one signature
|
|
221
|
+
- [ ] Shell from components.md, utilities from utilities.md; no invented class
|
|
222
|
+
- [ ] Styling ladder held: no `style` attribute, no `<style>` block, no custom rule doing a utility's job
|
|
223
|
+
- [ ] Plan tokens mapped to theme / --bs-* (no hex scatter); light and dark both shipped where both are offered
|
|
224
|
+
- [ ] Copy in user language, verbs consistent, empty/error/loading text useful
|
|
265
225
|
- [ ] Five states per data surface: ideal / empty / loading / partial / error
|
|
266
|
-
- [ ] Contrast measured in both themes: ≥ 4.5:1 information-bearing (small included), ≥ 3:1 marks and state chrome; meaning not color-alone
|
|
226
|
+
- [ ] Contrast composited and measured in both themes: ≥ 4.5:1 information-bearing (small included), ≥ 3:1 marks and state chrome; meaning not color-alone
|
|
227
|
+
- [ ] Tiers held: `-emphasis` for information-bearing status, solid buttons for real actions, no tone class inside a filled surface
|
|
267
228
|
- [ ] Every treatment resolved in the shipped cascade, not from docs memory
|
|
268
|
-
- [ ]
|
|
269
|
-
- [ ]
|
|
229
|
+
- [ ] Authored classes checked against that cascade; one glyph per meaning; every instrument's control failed
|
|
230
|
+
- [ ] Keyboard: focus visible, not obscured, targets ≥ 24px, icon controls named
|
|
231
|
+
- [ ] Reduced motion respected; every drag has a non-drag path
|
|
270
232
|
- [ ] Forms: labels visible, blur validation, error summary + inline, submit enabled
|
|
271
|
-
- [ ]
|
|
272
|
-
- [ ] States: hover/focus/disabled/invalid/active
|
|
273
|
-
- [ ] SPA hygiene: JS instances disposed on unmount
|
|
274
|
-
- [ ] Remove one unnecessary accessory
|
|
233
|
+
- [ ] Claimed breakpoints spot-checked; RTL-safe (start/end only)
|
|
234
|
+
- [ ] States present: hover / focus / disabled / invalid / active
|
|
235
|
+
- [ ] SPA hygiene: JS instances disposed on unmount, or framework wrappers used
|
|
275
236
|
- [ ] Rendered proof: captures at both viewports and both themes + an accessibility snapshot
|
|
276
237
|
```
|
|
277
|
-
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
## Key takeaways
|
|
281
|
-
|
|
282
|
-
- **Layered package** — craft and Bootstrap recipes live in the reference files; this file is the decision loop.
|
|
283
|
-
- **Distinctive** = subject + one signature + restraint — in product UI, signature in the chrome, discipline in the data.
|
|
284
|
-
- **Bootstrap** = reliable shipping across breakpoints, themes, and writing directions — extend through its variables and APIs, not against them.
|
|
285
|
-
- **Done** = design critique **and** production states **and** the accessibility bar — each proven by what rendered, in any environment.
|
|
@@ -79,8 +79,6 @@ CDN (5.3.8 is the current — and final — 5.3.x patch before 5.4):
|
|
|
79
79
|
<div class="row gx-5 gy-3">Independent horizontal/vertical gutters</div>
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
Mobile first: write for the smallest screen, add `sm`/`md`/`lg`/`xl`/`xxl` modifiers upward. Test every breakpoint you claim.
|
|
83
|
-
|
|
84
82
|
## Color Modes (light / dark / custom)
|
|
85
83
|
|
|
86
84
|
The 5.3 color-mode system replaces the old per-component dark variants.
|
|
@@ -90,7 +88,7 @@ The 5.3 color-mode system replaces the old per-component dark variants.
|
|
|
90
88
|
- `data-bs-theme="light|dark"` on `<html>` sets the mode globally; on any element it scopes the mode to that subtree (nested scopes win over ancestors). Default is light.
|
|
91
89
|
- Mode switching works by re-pointing root CSS variables — components never change their own rules. Core variables swapped per mode: `--bs-body-bg`, `--bs-body-color`, `--bs-emphasis-color`, `--bs-secondary-color`, `--bs-secondary-bg`, `--bs-tertiary-color`, `--bs-tertiary-bg`, `--bs-border-color`, `--bs-heading-color`, `--bs-link-color`.
|
|
92
90
|
- Each theme color also gets a mode-adaptive triplet — `--bs-{color}-text-emphasis`, `--bs-{color}-bg-subtle`, `--bs-{color}-border-subtle` — surfaced as `.text-{color}-emphasis`, `.bg-{color}-subtle`, `.border-{color}-subtle`. These are the workhorses for status UI that must read in both modes.
|
|
93
|
-
- **The triplet is a recipe, not a guarantee.** `text-{color}-emphasis` on `bg-{color}-subtle` is _designed_ to pass, and it usually does in stock Bootstrap — but the values are tokens, and a compatible skin redefines them. Resolve the actual computed values from the compiled cascade the page loads (the shipped CSS, dependency stylesheets included) and measure each pairing once per theme before you rely on it. A class with no rule of its own may still inherit one;
|
|
91
|
+
- **The triplet is a recipe, not a guarantee.** `text-{color}-emphasis` on `bg-{color}-subtle` is _designed_ to pass, and it usually does in stock Bootstrap — but the values are tokens, and a compatible skin redefines them. Resolve the actual computed values from the compiled cascade the page loads (the shipped CSS, dependency stylesheets included) and measure each pairing once per theme before you rely on it. A class with no rule of its own may still inherit one; never accept a value from documentation.
|
|
94
92
|
- Deprecated by this system: `.navbar-dark`, `.dropdown-menu-dark`, `.btn-close-white`, `.carousel-dark` → put `data-bs-theme="dark"` on the component or an ancestor instead.
|
|
95
93
|
|
|
96
94
|
### Author rules
|
|
@@ -337,19 +335,28 @@ Dropdowns, tooltips, and popovers require Popper — load `bootstrap.bundle.min.
|
|
|
337
335
|
|
|
338
336
|
### Baseline (every screen)
|
|
339
337
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
-
|
|
343
|
-
-
|
|
344
|
-
-
|
|
338
|
+
Hold the baseline in [SKILL.md](../SKILL.md) → Accessibility baseline. Its Bootstrap-specific parts:
|
|
339
|
+
|
|
340
|
+
- Skip link: `.visually-hidden-focusable` to `<main>`. Landmarks: `nav`, `main`, `aside`. Heading order `h1 → h2 → h3` without skips.
|
|
341
|
+
- Keep Bootstrap's focus rings; use the `.focus-ring` helper on custom interactive elements instead of removing outlines.
|
|
342
|
+
- `.visually-hidden` for screen-reader-only text; `.visually-hidden-focusable` for skip links. Never combine the two.
|
|
343
|
+
- Verify every color pairing against the shipped cascade. Bootstrap's own docs warn that parts of the default palette fall short.
|
|
344
|
+
|
|
345
|
+
**Measuring the bars:**
|
|
346
|
+
|
|
347
|
+
- Hold the bars from [SKILL.md](../SKILL.md) → Surfaces, color, contrast: **≥ 4.5:1** for everything information-bearing, **≥ 3:1** for textless marks and state chrome. WCAG 2.2 permits 3:1 for large text; this package does not — size grants no lower tier.
|
|
348
|
+
- Measure in **both themes**, from the compiled cascade, never from the token names. A pairing that passes in light routinely fails in dark, and a skin's values are its own.
|
|
349
|
+
- Focus rings and hover fills are UI graphics: they are in scope for the 3:1 bar.
|
|
350
|
+
- Disabled controls are exempt from the bars by the spec. That exemption covers legibility, not meaning — see [Destructive actions](#destructive-actions) for the one disabled state that still has to change color.
|
|
351
|
+
|
|
352
|
+
**The instrument.** Bootstrap paints in translucent layers: a card header and footer are a 3% tint of the body color over the card's own background. A reader that stops at the first painted ancestor and drops its alpha treats that tint as full-strength paint, and is then wrong in **both** directions — it green-lights a pairing nobody can read, and it red-flags one that reads fine. Use a reader that:
|
|
345
353
|
|
|
346
|
-
|
|
354
|
+
- collects every painted layer from the element upward to the first opaque one, then composites them top over bottom (Porter-Duff `over`) onto that opaque base;
|
|
355
|
+
- composites a translucent foreground over that result before taking the ratio, rather than reading the declared color;
|
|
356
|
+
- measures both themes in one run, since the theme swap re-points the tokens under every layer;
|
|
357
|
+
- carries a negative control drawn from outside the population it covers — a pairing known to fail — and voids the run if that control passes.
|
|
347
358
|
|
|
348
|
-
|
|
349
|
-
- **≥ 4.5:1** for everything information-bearing — including `small`, captions, meta lines, and timestamps. Small size is not a licence for a lower tier; `text-body-tertiary` is decoration, `text-body-secondary` is the floor for text a user must read.
|
|
350
|
-
- **≥ 3:1** for textless marks and state indicators — status glyphs, rings, dividers that carry meaning — and for the hover/focus chrome that signals state. Focus rings and hover fills are UI graphics: they are in scope, and they are the ones nobody measures.
|
|
351
|
-
- Disabled controls are exempt from the bars by the spec. That exemption is about legibility, not about meaning: see [Destructive actions](#destructive-actions) for the one disabled state that still has to change color.
|
|
352
|
-
- `.visually-hidden` for SR-only text; `.visually-hidden-focusable` for skip links (never combine the two).
|
|
359
|
+
Wire the reader into the suite once it has settled a question.
|
|
353
360
|
|
|
354
361
|
### WCAG 2.2 deltas that bite dense app UI
|
|
355
362
|
|
|
@@ -398,7 +405,7 @@ The Bootstrap implementation — a responsive offcanvas that renders inline abov
|
|
|
398
405
|
<header class="navbar bg-body-tertiary border-bottom sticky-top">
|
|
399
406
|
<div class="container-fluid">
|
|
400
407
|
<button
|
|
401
|
-
class="btn btn-
|
|
408
|
+
class="btn btn-secondary d-lg-none"
|
|
402
409
|
type="button"
|
|
403
410
|
data-bs-toggle="offcanvas"
|
|
404
411
|
data-bs-target="#appSidebar"
|
|
@@ -506,20 +513,20 @@ Give header cells an **opaque background** (`bg-body-secondary` or a table varia
|
|
|
506
513
|
### Wizards & multi-step forms
|
|
507
514
|
|
|
508
515
|
- Show step progress: current position, total, and step names ("Step 2 of 4 — Billing"); `list-group-numbered` or a simple nav renders it honestly.
|
|
509
|
-
- Validate per
|
|
516
|
+
- Validate per step before advancing; never let a step advance carrying invalid data.
|
|
510
517
|
- Back never loses data. Persist partial state (save-and-resume) for anything beyond ~3 steps or that crosses sessions.
|
|
511
518
|
- Never re-ask what a previous step collected (Redundant Entry, 3.3.7) — carry it forward or offer "same as above".
|
|
512
519
|
- Last step: a review summary with per-section edit links, then one clearly-named commit action ("Create account", not "Submit").
|
|
513
520
|
|
|
514
521
|
### The five states
|
|
515
522
|
|
|
516
|
-
Design **all five** for every data surface: ideal (populated), empty, loading, partial, error. A component isn't done until all five exist
|
|
523
|
+
Design **all five** for every data surface: ideal (populated), empty, loading, partial, error. A component isn't done until all five exist.
|
|
517
524
|
|
|
518
525
|
- **Skeleton vs spinner:** skeleton (`placeholder` + `placeholder-glow`) when you know the content's shape and it fills a region — tables, cards, detail panes — because it holds layout and shortens perceived wait. Spinner for short, indeterminate, or in-control waits (inside a button, a small inline fetch).
|
|
519
526
|
- **Thresholds (guidance):** under ~1s show nothing — a flashed loader is worse than none; ~1–10s show a spinner or skeleton; beyond ~10s show determinate progress (percent or step) so it doesn't feel hung.
|
|
520
527
|
- **Optimistic vs pessimistic:** apply UI immediately and reconcile (rolling back loudly on failure) for reversible high-frequency actions — toggles, stars, reorders. Await confirmation for money, audited records, and anything a rollback would confuse.
|
|
521
|
-
- **Empty states** invite the next action (button + one line of why)
|
|
522
|
-
- **Every error state carries a keyboard-reachable retry
|
|
528
|
+
- **Empty states** invite the next action (button + one line of why). Never-had-data and filtered-empty are different states with different escapes — never ship one generic "nothing here".
|
|
529
|
+
- **Every error state states what failed and how to fix it**, carries a keyboard-reachable retry in place, and preserves surrounding context — a body fetch failure must not blow away the toolbar and filters.
|
|
523
530
|
|
|
524
531
|
### Feedback discipline
|
|
525
532
|
|
|
@@ -540,9 +547,9 @@ Match friction to reversibility × blast radius:
|
|
|
540
547
|
2. **Confirm dialog** for irreversible-but-scoped operations. Restate the specific consequence ("This permanently deletes 3 invoices"), verb-labeled buttons ("Delete invoices" / "Cancel" — never Yes/No), destructive action visually separated from safe; `alertdialog` semantics; focus lands on the safe action.
|
|
541
548
|
3. **Type-to-confirm** (type the entity name) only for high-blast-radius irreversible operations — delete an org, drop a dataset.
|
|
542
549
|
|
|
543
|
-
Don't type-gate a single-row delete; don't one-tap a tenant wipe.
|
|
550
|
+
Don't type-gate a single-row delete; don't one-tap a tenant wipe. Confirm only where this ladder calls for it — a confirmation on every action gets clicked through.
|
|
544
551
|
|
|
545
|
-
**
|
|
552
|
+
**Neutralize a disabled destructive control.** `btn-danger` at full saturation reads as armed whatever the `disabled` attribute says, and the contrast exemption for disabled controls does not excuse it. While the action is unavailable, drop to the neutral or outline variant (or let the disabled state mute the fill) so the color stops promising an action, and say _why_ it is unavailable in text the assistive layer reaches: `aria-describedby` pointing at the reason, with `title` only as the pointer-user convenience on top. Never use `title` alone — it never reaches a keyboard or screen-reader user, and it disappears on touch.
|
|
546
553
|
|
|
547
554
|
## RTL
|
|
548
555
|
|
|
@@ -570,7 +577,7 @@ Bootstrap has **no** combobox/autocomplete, date picker, multi-select tags input
|
|
|
570
577
|
|
|
571
578
|
- **Reach for native first:** `<input type="date">`, `<datalist>` for light autocomplete, `<select multiple>` where acceptable. Native widgets bring keyboard and AT behavior free.
|
|
572
579
|
- **Reach for an established accessible library second** when the product genuinely needs the richer widget (combobox with async search, spreadsheet grid, drag-reorder tree). Budget for auditing it against the APG contract.
|
|
573
|
-
- **Hand-roll last**, only with the APG contract in hand ([Accessibility](#accessibility) → Pattern contracts) and
|
|
580
|
+
- **Hand-roll last**, only with the APG contract in hand ([Accessibility](#accessibility) → Pattern contracts) and budget for the _keyboard_ half, which is most of the work.
|
|
574
581
|
- Never fake it: a `.dropdown-menu` posing as a select, a `<div>` grid with click handlers, or a scroll-anchor "wizard" each break keyboard and AT users in ways a demo never shows.
|
|
575
582
|
|
|
576
583
|
## Common Layout Patterns
|