@orkestrel/scaffold 0.0.65 → 0.0.67
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 +58 -38
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +63 -33
- package/dist/host/agents/skills/enterprise-bootstrap/references/color-modes.md +131 -16
- package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +21 -13
- package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +41 -46
- package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +37 -29
- package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +51 -16
- package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +57 -7
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +11 -6
- package/dist/host/claude/agents/orkestrel.md +24 -24
- package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +1 -1
- package/dist/host/manifest.json +12 -12
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/src/core/index.cjs +8 -8
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.js +8 -8
- package/dist/src/core/index.js.map +1 -1
- package/package.json +8 -8
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Color modes and inheritance
|
|
2
2
|
|
|
3
|
-
> Part of the `enterprise-bootstrap`
|
|
3
|
+
> Part of the `enterprise-bootstrap` skill. Use for foreground ownership, adaptive surfaces,
|
|
4
4
|
> nested themes, and color-mode repairs. Take the contrast bars from [SKILL.md](../SKILL.md).
|
|
5
5
|
|
|
6
6
|
## Contents
|
|
7
7
|
|
|
8
8
|
- [Choose the surface](#choose-the-surface)
|
|
9
|
+
- [Fixed and adaptive classes](#fixed-and-adaptive-classes)
|
|
9
10
|
- [Text tiers](#text-tiers)
|
|
10
11
|
- [Preserve the cascade](#preserve-the-cascade)
|
|
11
12
|
- [Scope the mode](#scope-the-mode)
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
|
|
18
19
|
Default to inherited text. When ordinary content owns no background, add no foreground override.
|
|
19
20
|
For quiet containers and status, prefer adaptive backgrounds without an added text-color class.
|
|
20
|
-
Treat this as the
|
|
21
|
+
Treat this as the skill's default, not a claim that Bootstrap forbids its documented emphasis pairs.
|
|
21
22
|
|
|
22
23
|
| Context | Use | Refuse by default |
|
|
23
24
|
| -------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
@@ -42,12 +43,115 @@ Measure the inherited result on its actual background. A subtle fill changes onl
|
|
|
42
43
|
it cannot repair a fixed, translucent, or inverse foreground inherited from an ancestor. Remove
|
|
43
44
|
that conflict or establish an owned surface boundary before adding a leaf override.
|
|
44
45
|
|
|
46
|
+
## Fixed and adaptive classes
|
|
47
|
+
|
|
48
|
+
Every Bootstrap color class resolves through one of two kinds of variable. **Adaptive** classes
|
|
49
|
+
read a variable the `[data-bs-theme=dark]` block redefines — `--bs-body-color`/`-bg`,
|
|
50
|
+
`--bs-emphasis-color`, `--bs-secondary-color`/`-bg`, `--bs-tertiary-color`/`-bg`,
|
|
51
|
+
`--bs-*-text-emphasis`, `--bs-*-bg-subtle`, `--bs-*-border-subtle`, `--bs-border-color`,
|
|
52
|
+
`--bs-link-color` — and change with the mode. **Fixed** classes read `--bs-{theme}-rgb`,
|
|
53
|
+
`--bs-white-rgb`, `--bs-black-rgb`, `--bs-light-rgb`, or `--bs-dark-rgb`, which no mode
|
|
54
|
+
redefines, and paint the same value in light and dark.
|
|
55
|
+
|
|
56
|
+
This table classifies a class by the variable it reads, and nothing else. Deprecation is a separate
|
|
57
|
+
axis: `text-muted` reads the adaptive secondary color and is deprecated, so it classifies as
|
|
58
|
+
adaptive here and still gives way to `text-body-secondary` on its deprecation.
|
|
59
|
+
|
|
60
|
+
| Family | Adaptive | Fixed |
|
|
61
|
+
| ---------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| Text | `text-body`, `text-body-emphasis`, `text-body-secondary`, `text-body-tertiary`, `text-muted` (alias), `text-*-emphasis` | `text-primary` … `text-danger`, `text-light`, `text-dark`, `text-white`, `text-black`, `text-white-50`, `text-black-50` |
|
|
63
|
+
| Background | `bg-body`, `bg-body-secondary`, `bg-body-tertiary`, `bg-*-subtle` | `bg-primary` … `bg-danger`, `bg-light`, `bg-dark`, `bg-white`, `bg-black`, `bg-gradient` |
|
|
64
|
+
| Border | `border` (default color, decorative at 1.3:1), `border-*-subtle` | `border-primary` … `border-dark`, `border-white`, `border-black` |
|
|
65
|
+
| Links | plain `<a>`, `btn-link`, `link-body-emphasis` | `link-primary` … `link-dark`, `link-light` |
|
|
66
|
+
| Own pairs | `.alert-*`, `.list-group-item-*`, `.form-control`, `.card`, `.dropdown-menu`, `.modal-content`, `.toast`, `.btn-close` | `text-bg-*`, solid `btn-*`, `btn-light`/`btn-dark`, `table-*` row variants, `progress-bar`, active nav/list/page states |
|
|
67
|
+
| No pair | — | `btn-outline-*`: a fixed label and border on whatever surface the page has |
|
|
68
|
+
|
|
69
|
+
**Pair like with like.** Adaptive text on an adaptive surface; fixed text on a fixed fill. Treat
|
|
70
|
+
every mixed pair as unproven until measured: some clear one mode and fail the other, and some fail
|
|
71
|
+
light and dark alike. Read the outcome from the measurement, never from the mixture.
|
|
72
|
+
|
|
73
|
+
The following readings bound stock Bootstrap 5.3.8 and nothing else. A declared theme, a skin, or an
|
|
74
|
+
overridden token re-points every value in the table, so measure that theme yourself rather than
|
|
75
|
+
carrying a stock number into it. Light | dark:
|
|
76
|
+
|
|
77
|
+
| Mixed pair | Light | Dark |
|
|
78
|
+
| ------------------------------------------------------- | ------------- | ------------- |
|
|
79
|
+
| `text-primary` / `-success` / `-danger` on `bg-body` | 4.5 | 3.4 ✗ |
|
|
80
|
+
| `text-secondary` on `bg-body` | 4.7 | 3.3 ✗ |
|
|
81
|
+
| `text-warning` / `text-info` on `bg-body` | 1.6 ✗ / 2.0 ✗ | 9.5 / 7.9 |
|
|
82
|
+
| `text-dark`, `text-black` on `bg-body` | 15.4 / 21.0 | 1.0 ✗ / 1.4 ✗ |
|
|
83
|
+
| `text-white`, `text-light` on `bg-body` | 1.0 ✗ / 1.1 ✗ | 15.4 / 14.6 |
|
|
84
|
+
| Inherited text on `bg-light` / `bg-white` | 14.6 / 15.4 | 1.2 ✗ / 1.3 ✗ |
|
|
85
|
+
| Inherited text on `bg-dark` / `bg-black` | 1.0 ✗ / 1.4 ✗ | 11.8 / 16.1 |
|
|
86
|
+
| Inherited text on `bg-primary` / `-danger` / `-success` | 3.4 ✗ | 3.5 ✗ |
|
|
87
|
+
| `btn-outline-secondary` label on `bg-body` | 4.7 | 3.3 ✗ |
|
|
88
|
+
| `btn-outline-primary` label on `bg-body` | 4.5 | 3.4 ✗ |
|
|
89
|
+
| `link-primary` (`--bs-primary-rgb`) on `bg-body` | 4.5 | 3.4 ✗ |
|
|
90
|
+
|
|
91
|
+
The adaptive pairs hold: `text-*-emphasis` on `bg-body` is 6.1–13.6 in light and dark and on
|
|
92
|
+
`bg-*-subtle` 7.2–10.8, with one exception — `text-dark-emphasis` is 3.7 on the dark body; use
|
|
93
|
+
`text-body-emphasis` for neutral emphasis. A plain link is 4.5 light and 6.4 dark because
|
|
94
|
+
`--bs-link-color` is redefined; `text-primary` and `link-primary` are not.
|
|
95
|
+
|
|
96
|
+
Rules:
|
|
97
|
+
|
|
98
|
+
- Default to adaptive on adaptive: inherited text on `bg-body*` or `bg-*-subtle`; status text as
|
|
99
|
+
`text-*-emphasis`; links as plain links or `link-body-emphasis`.
|
|
100
|
+
- A fixed fill needs a fixed foreground you can name: `text-bg-*` (white on primary, secondary,
|
|
101
|
+
success, and danger at 4.5–4.7; black on warning, info, and light; white on dark), the
|
|
102
|
+
component's own foreground, or a `data-bs-theme` scope that carries `text-body` on the same
|
|
103
|
+
element. Inherited text on a fixed fill is never a pair.
|
|
104
|
+
- A `data-bs-theme` scope redefines variables only. Text color is painted once, on `<body>`, from
|
|
105
|
+
the outer mode, and plain descendants inherit that computed color — not the variable. Inside
|
|
106
|
+
the scope only rules that resolve `color` through a variable re-align: `text-body*`,
|
|
107
|
+
`text-*-emphasis`, buttons, links, `list-group-item`, form controls, `btn-close`. A paragraph,
|
|
108
|
+
a `span`, a heading without a text class, and `.card` (its `--bs-card-color` is empty, so it
|
|
109
|
+
inherits) keep the outer mode's color. The symptom is a headline in `text-body-emphasis`
|
|
110
|
+
reading correctly beside a paragraph that vanishes. Restate the surface and the foreground on
|
|
111
|
+
the scope element: take `bg-body text-body` where the fill must follow the mode, and a fixed
|
|
112
|
+
`bg-*` plus `text-body` where it must not
|
|
113
|
+
(`<section class="bg-dark text-body" data-bs-theme="dark">`).
|
|
114
|
+
Check components that leave the scope: dropdown menus, and dialogs mounted at the root.
|
|
115
|
+
- Outline buttons carry a fixed label. Lift the neutral one once at the theme root —
|
|
116
|
+
`.btn-outline-secondary { --bs-btn-color: var(--bs-emphasis-color); }` — its `#6c757d` border
|
|
117
|
+
still clears the 3:1 boundary bar in light and dark (4.7 | 3.3). For an adaptive primary outline set
|
|
118
|
+
`--bs-btn-color` and `--bs-btn-border-color` to `var(--bs-link-color)`; `btn-link` already adapts.
|
|
119
|
+
`btn-outline-light` and `btn-outline-dark` belong only inside a fixed fill of the opposite tone.
|
|
120
|
+
- Shadows are fixed black alphas and nearly vanish on dark surfaces; carry depth with the
|
|
121
|
+
`bg-body*` steps in dark mode.
|
|
122
|
+
- Icon fonts and `fill="currentColor"` follow the text; a literal hex in an inline SVG or an
|
|
123
|
+
`<img>` is fixed. Give logos and illustrations a scoped variant or a `currentColor` path.
|
|
124
|
+
|
|
125
|
+
| Legacy or mixed | Adaptive replacement |
|
|
126
|
+
| ------------------------------------------------ | ------------------------------------------------------------------------- |
|
|
127
|
+
| `bg-light` panel | `bg-body-tertiary` |
|
|
128
|
+
| `bg-white` | `bg-body` |
|
|
129
|
+
| Neutral `bg-dark` region | `bg-dark` with `data-bs-theme="dark"`, or `bg-body` inside a dark scope |
|
|
130
|
+
| `text-dark`, `text-black` | `text-body-emphasis`, or inherit |
|
|
131
|
+
| `text-white` on an adaptive surface | inherit |
|
|
132
|
+
| `text-muted`, `text-black-50`, `text-white-50` | `text-body-secondary` |
|
|
133
|
+
| `text-primary` … `text-danger` as running text | `text-*-emphasis`, measured |
|
|
134
|
+
| `border-light`, `border-dark` | `border`, or `border-*-subtle` |
|
|
135
|
+
| `navbar-light`, `navbar-dark`, `btn-close-white` | `data-bs-theme` on the navbar or region |
|
|
136
|
+
| `badge bg-primary` with inherited text | `badge text-bg-primary`, or `badge bg-primary-subtle` with inherited text |
|
|
137
|
+
| `link-primary` … `link-dark` | plain link, or `link-body-emphasis` |
|
|
138
|
+
|
|
139
|
+
Read that table as a replacement map, not as a classification. Replace `bg-light`, `bg-white`,
|
|
140
|
+
`bg-dark`, `text-dark`, `text-black`, `text-white`, `text-black-50`, `text-white-50`,
|
|
141
|
+
`border-light`, `border-dark`, and the colored `text-*` and `link-*` families because they are
|
|
142
|
+
fixed. Replace `text-muted`, `navbar-light`, `navbar-dark`, and `btn-close-white` because 5.3
|
|
143
|
+
deprecates them — `text-muted` already adapts, so its replacement closes a deprecation rather than
|
|
144
|
+
a contrast defect.
|
|
145
|
+
|
|
45
146
|
## Text tiers
|
|
46
147
|
|
|
47
|
-
Stock Bootstrap ships
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
148
|
+
Stock Bootstrap ships the inherited body color and `text-body-secondary` as its readable tiers;
|
|
149
|
+
`text-body-tertiary` is not readable text. `--bs-secondary-color` is the body color at 75 % alpha
|
|
150
|
+
and `--bs-tertiary-color` at 50 % alpha, so each composites against whatever sits beneath it.
|
|
151
|
+
|
|
152
|
+
The following readings bound stock 5.3.8 on its own body surfaces (`bg-body` / `bg-body-tertiary` /
|
|
153
|
+
`bg-body-secondary`). Measure a declared theme or a skin yourself; its tokens re-point every value
|
|
154
|
+
here.
|
|
51
155
|
|
|
52
156
|
| Tier | Light | Dark | 4.5:1 bar |
|
|
53
157
|
| --------------------- | ------------------ | ----------------- | ---------------- |
|
|
@@ -57,19 +161,19 @@ beneath them. Measured on the three stock body surfaces (`bg-body` / `bg-body-te
|
|
|
57
161
|
|
|
58
162
|
- Use `text-body-secondary` as the one shipped quiet tier. Use `text-body-tertiary` for decoration
|
|
59
163
|
or disabled chrome only; it never carries a caption, timestamp, or count someone reads.
|
|
60
|
-
- Declare
|
|
164
|
+
- Declare any further readable tier as an opaque token — override `$body-tertiary-color` and
|
|
61
165
|
`$body-tertiary-color-dark`, or add a semantic token — and measure it on each surface it sits on.
|
|
62
166
|
The opaque `text-secondary` (`$gray-600`) clears 4.5:1 only on pure white and does not adapt.
|
|
63
|
-
-
|
|
167
|
+
- Component variables consume the translucent secondary color — placeholders,
|
|
64
168
|
`.form-text`, table captions, breadcrumb dividers and the active crumb, toast headers, figure
|
|
65
|
-
captions, list-group action text,
|
|
169
|
+
captions, list-group action text, and disabled states — and inherit this table.
|
|
66
170
|
- On a colored fill, `text-body-secondary` composites the body color over the hue — grey on color —
|
|
67
171
|
and `text-white-50` or `text-opacity-*` let the fill show through the glyphs. The quiet tone on a
|
|
68
172
|
fill is the same hue at lower contrast: keep the owning component's foreground and quiet a line
|
|
69
173
|
with weight or size, or declare a same-hue token (for the stock blue, the `$blue-200`–`$blue-300`
|
|
70
174
|
region in light mode) through the component's own variable and measure it.
|
|
71
175
|
- For a tinted region, use the shipped same-hue pair: `text-*-emphasis` on `bg-*-subtle` (about
|
|
72
|
-
10:1 for primary in the stock light theme), adapting in
|
|
176
|
+
10:1 for primary in the stock light theme), adapting in light and dark. Keep `text-bg-*` solids for
|
|
73
177
|
the primary element.
|
|
74
178
|
|
|
75
179
|
When checking utility behavior, see Bootstrap's [Background](https://getbootstrap.com/docs/5.3/utilities/background/)
|
|
@@ -119,13 +223,22 @@ Use the host's mode controller. With Bootstrap's attribute strategy, set the res
|
|
|
119
223
|
`dark` value on `<html>`; scope an intentional exception with `data-bs-theme` on its boundary.
|
|
120
224
|
Do not pin components to dark merely because the page happens to be dark during development.
|
|
121
225
|
|
|
122
|
-
|
|
226
|
+
A nested scope changes variables, not painted colors: `color` is set on `<body>` by the outer
|
|
227
|
+
mode and inherited as a computed value, so plain text inside the scope stays the outer color
|
|
228
|
+
until the boundary restates it. Establish both the surface and the foreground on the scope
|
|
229
|
+
element — never rely on the attribute alone, and never fix the symptom by sprinkling `text-white`
|
|
230
|
+
on individual descendants:
|
|
123
231
|
|
|
124
232
|
```html
|
|
125
233
|
<section data-bs-theme="dark" class="bg-body text-body p-3" aria-labelledby="preview-heading">
|
|
126
234
|
<h2 id="preview-heading" class="h6">Preview</h2>
|
|
127
235
|
<div class="bg-primary-subtle rounded p-3">This text inherits from the preview.</div>
|
|
128
236
|
</section>
|
|
237
|
+
|
|
238
|
+
<!-- Fixed fill or image that must not follow the page mode: keep bg-*, still restate text-body. -->
|
|
239
|
+
<section data-bs-theme="dark" class="bg-dark text-body py-5" aria-labelledby="hero-heading">
|
|
240
|
+
…
|
|
241
|
+
</section>
|
|
129
242
|
```
|
|
130
243
|
|
|
131
244
|
Keep the body foreground at that owned boundary; add no text-color classes to its ordinary
|
|
@@ -166,7 +279,8 @@ Do not use `.badge bg-success-subtle` alone: stock `.badge` supplies a white for
|
|
|
166
279
|
inherited body text. Do not mistake the absence of a `text-*` class for the absence of a color
|
|
167
280
|
rule. Confirm the skin's badge rule; see [Badges](https://getbootstrap.com/docs/5.3/components/badge/).
|
|
168
281
|
Keep a removable tag's close button in the same mode and measure its hit area; a badge's small
|
|
169
|
-
font must not shrink the control below the
|
|
282
|
+
font must not shrink the control below the target floor in
|
|
283
|
+
[bootstrap-reference.md](bootstrap-reference.md) → WCAG 2.2 requirements for app UI.
|
|
170
284
|
|
|
171
285
|
### Alerts, buttons, and selection
|
|
172
286
|
|
|
@@ -214,12 +328,12 @@ Stock ramps are mechanical: `$blue-100…400` are `tint-color` mixes with white
|
|
|
214
328
|
are `shade-color` mixes with black, and the `-text-emphasis` / `-bg-subtle` / `-border-subtle`
|
|
215
329
|
triads are the same mixes. Hue stays fixed and saturation can only fall, so any brand base short
|
|
216
330
|
of full saturation washes out at the light end and goes muddy at the dark end; stock blue survives
|
|
217
|
-
only because its base is 98 % saturated. For a brand color, override the
|
|
218
|
-
variables and the
|
|
331
|
+
only because its base is 98 % saturated. For a brand color, override the `$brand-100…900` shade
|
|
332
|
+
variables and the triad variables (`$brand-text-emphasis`, `-bg-subtle`, `-border-subtle`,
|
|
219
333
|
each with its `-dark` twin) with hand-picked values before `variables` is imported; every
|
|
220
334
|
`.alert-brand`, `bg-brand-subtle`, `text-brand-emphasis`, and `table-brand` then consumes them.
|
|
221
335
|
Stock greys sit at hue 210° with 7–17 % saturation — cool, matched to the stock blue. For a warm
|
|
222
|
-
brand, override `$gray-100…900` as a complete set
|
|
336
|
+
brand, override `$gray-100…900` as a complete set at one hue and temperature; never drop
|
|
223
337
|
one warm grey into the cool set. Take the picking method from
|
|
224
338
|
[frontend-design.md](frontend-design.md) → Color as a constrained system.
|
|
225
339
|
|
|
@@ -237,5 +351,6 @@ Do not infer their foregrounds from a desktop capture or from the theme attribut
|
|
|
237
351
|
|
|
238
352
|
Run [Color-mode inheritance](inspection.md#color-mode-inheritance) and the applicable contrast,
|
|
239
353
|
target, and rendered-review checks against the loaded Bootstrap build and skin. Exercise the
|
|
240
|
-
existing mounted UI through
|
|
354
|
+
existing mounted UI through every declared mode and back, including supported local scopes and
|
|
355
|
+
overlays.
|
|
241
356
|
Report actual coverage; do not claim a host application pass from documentation or a stock fixture.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bootstrap 5 Component Reference
|
|
2
2
|
|
|
3
|
-
> Part of the `enterprise-bootstrap`
|
|
3
|
+
> Part of the `enterprise-bootstrap` skill. Bootstrap **5.3.x** component
|
|
4
4
|
> markup + enterprise selection notes. Utility classes: [utilities.md](utilities.md).
|
|
5
5
|
> Theming, forms deep-dive, JS lifecycle, patterns: [bootstrap-reference.md](bootstrap-reference.md).
|
|
6
6
|
|
|
@@ -158,9 +158,9 @@ Reserve the solid variants for intentional prominence or a counter with its own
|
|
|
158
158
|
|
|
159
159
|
For an intentional solid badge, use a measured `text-bg-*` pair rather than a solid background alone. Its foreground is selected at Sass build time, not recalculated on a runtime theme change. Never make color the only carrier of meaning; keep visible text or an accessible label.
|
|
160
160
|
|
|
161
|
-
**A badge is never a textless mark.** Stock Bootstrap ships `.badge:empty { display: none }`, so an empty `<span class="badge">` used as a status dot renders nothing
|
|
161
|
+
**A badge is never a textless mark.** Stock Bootstrap ships `.badge:empty { display: none }`, so an empty `<span class="badge">` used as a status dot renders nothing. Draw a textless status mark as an **icon glyph** (see [Icons](#icons) → Status glyph marks), never as a stripped badge, and confirm the mark in a capture — source review cannot see the missing paint.
|
|
162
162
|
|
|
163
|
-
State a badge's intended fill and inspect the skin. For a quiet badge use `bg-*-subtle text-reset`; for an unfilled badge use `bg-transparent text-reset`. Verify the inherited foreground on that actual surface in
|
|
163
|
+
State a badge's intended fill and inspect the skin. For a quiet badge use `bg-*-subtle text-reset`; for an unfilled badge use `bg-transparent text-reset`. Verify the inherited foreground on that actual surface in every declared theme. A fill-only utility does not remove `.badge`'s white text; see [color-modes.md](color-modes.md) → Badges and removable tags.
|
|
164
164
|
|
|
165
165
|
A badge reporting an in-flight request is a live region: `role="status"` on the badge (or on the small wrapper that holds it) announces the settled state politely without stealing focus. Reserve `role="alert"` for urgent results ([Alerts](#alerts)).
|
|
166
166
|
|
|
@@ -180,6 +180,8 @@ The current page is `aria-current="page"` and not a link. Use breadcrumbs only f
|
|
|
180
180
|
|
|
181
181
|
### Buttons
|
|
182
182
|
|
|
183
|
+
Solid variants own a fixed foreground/background pair and read the same in light and dark (primary and danger at 4.5:1, on the line). Outline variants paint a fixed label on the adaptive page surface: `btn-outline-secondary` is 4.7:1 on the light body and 3.3:1 on the dark one, `btn-outline-primary` 4.5 | 3.4. Lift the neutral once at the theme root — `.btn-outline-secondary { --bs-btn-color: var(--bs-emphasis-color); }` — and give an adaptive primary outline `--bs-btn-color`/`--bs-btn-border-color: var(--bs-link-color)`; `btn-link` adapts already. `btn-light`/`btn-dark` and `btn-outline-light`/`-dark` belong inside fixed fills of the opposite tone ([color-modes.md](color-modes.md) → Fixed and adaptive classes).
|
|
184
|
+
|
|
183
185
|
```html
|
|
184
186
|
<button type="button" class="btn btn-primary">Primary</button>
|
|
185
187
|
<button type="button" class="btn btn-secondary">Secondary</button>
|
|
@@ -203,9 +205,9 @@ The current page is `aria-current="page"` and not a link. Use breadcrumbs only f
|
|
|
203
205
|
<button type="button" class="btn btn-primary" data-bs-toggle="button">Toggle</button>
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
Icon-only buttons need `aria-label` and a
|
|
208
|
+
Icon-only buttons need `aria-label` and a target meeting the floor in [bootstrap-reference.md](bootstrap-reference.md) → WCAG 2.2 requirements for app UI — `btn-sm` icon clusters in toolbars are the common violation; pad rather than shrink.
|
|
207
209
|
|
|
208
|
-
The
|
|
210
|
+
The shipped sizes scale padding faster than font — `btn-sm` 4/8 px at 14 px, `btn` 6/12 at 16 px, `btn-lg` 8/16 at 20 px — so a large button reads as larger, not zoomed. Use them as shipped; do not derive another size with `em` padding. Weight is `$font-weight-normal`; `fw-semibold` on a button is a deliberate emphasis choice, not a default.
|
|
209
211
|
|
|
210
212
|
Choose action rank, then a variant whose rest, hover, focus, active/checked, and disabled treatment works on its actual surface ([SKILL.md](../SKILL.md) → Hierarchy & actions). Do not override native button states with background or text utilities.
|
|
211
213
|
|
|
@@ -504,6 +506,8 @@ Bootstrap's modal enforces focus, adds `role="dialog"`/`aria-modal="true"`, clos
|
|
|
504
506
|
|
|
505
507
|
### Navbar
|
|
506
508
|
|
|
509
|
+
Stock 5.3 navbars are adaptive on `bg-body-tertiary`. `navbar-light` and `navbar-dark` are deprecated: a dark brand bar is `navbar bg-dark` with `data-bs-theme="dark"` plus `text-body` on the same element — the scope re-aligns the toggler icon, links, and form controls, which resolve their own color variables, while plain text inherits the outer mode's painted color — never `bg-dark` alone with inherited text. Match `navbar-expand-{bp}` to where the full destination set fits; the toggler must remain a named `navbar-toggler` with `aria-controls` and `aria-expanded`.
|
|
510
|
+
|
|
507
511
|
```html
|
|
508
512
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
|
509
513
|
<div class="container-fluid">
|
|
@@ -704,7 +708,7 @@ when the presentation changes.
|
|
|
704
708
|
<p aria-hidden="true">
|
|
705
709
|
<span class="placeholder col-6"></span>
|
|
706
710
|
<span class="placeholder w-75"></span>
|
|
707
|
-
<span class="placeholder
|
|
711
|
+
<span class="placeholder w-25"></span>
|
|
708
712
|
</p>
|
|
709
713
|
|
|
710
714
|
<span class="placeholder col-12 placeholder-lg">Large</span>
|
|
@@ -717,7 +721,7 @@ when the presentation changes.
|
|
|
717
721
|
<button class="btn btn-primary disabled placeholder col-4" aria-hidden="true"></button>
|
|
718
722
|
```
|
|
719
723
|
|
|
720
|
-
Always wrap skeletons in `aria-hidden="true"` — they are visual scaffolding, not content. Skeleton-vs-spinner decision rules: [bootstrap-reference.md](bootstrap-reference.md) → The data states.
|
|
724
|
+
Always wrap skeletons in `aria-hidden="true"` — they are visual scaffolding, not content. Size a placeholder with `col-*` or a shipped width utility; a skeleton width is a layout decision, not a runtime value, so it never takes a `style` attribute. Skeleton-vs-spinner decision rules: [bootstrap-reference.md](bootstrap-reference.md) → The data states.
|
|
721
725
|
|
|
722
726
|
### Popover (Requires Popper.js)
|
|
723
727
|
|
|
@@ -748,6 +752,8 @@ Popovers are **opt-in**: they do nothing until initialized in JS (see [JavaScrip
|
|
|
748
752
|
|
|
749
753
|
### Progress
|
|
750
754
|
|
|
755
|
+
For dynamic progress, have the host script set `width` on `.progress-bar` in a standalone `.progress`, or on each `.progress` segment in `.progress-stacked`. Derive the painted width and the matching `aria-valuenow` from the same progress value. Record that script by name and purpose under [inspection.md](inspection.md) → Style escapes. Exempt only those elements' producer-written `width` declarations. Keep all other widths in shipped utilities or the project stylesheet.
|
|
756
|
+
|
|
751
757
|
5.3 markup — `role="progressbar"` and the `aria-value*` attributes go on the **outer `.progress`**, not the inner bar:
|
|
752
758
|
|
|
753
759
|
```html
|
|
@@ -873,6 +879,8 @@ Gotcha: the spied element must be a scroll container (height/overflow, or focusa
|
|
|
873
879
|
|
|
874
880
|
### Tables
|
|
875
881
|
|
|
882
|
+
`table-*` row variants are fixed pairs (`--bs-table-bg` tint with `--bs-table-color: #000`) and read as light rows inside a dark table; prefer a `bg-*-subtle` status mark in a cell. `table-responsive` is `overflow-x: auto` only — give the wrapper `role="region"`, an `aria-label`, and `tabindex="0"` so keyboard users can reach its far edge, and add `data-bs-popper-config='{"strategy":"fixed"}'` to any dropdown toggle inside it ([responsive-layout.md](responsive-layout.md) → Bootstrap's responsive surface).
|
|
883
|
+
|
|
876
884
|
```html
|
|
877
885
|
<table class="table">
|
|
878
886
|
<caption class="visually-hidden">
|
|
@@ -972,7 +980,7 @@ Toasts are **opt-in** — hidden until `.show()` is called (or shown through a t
|
|
|
972
980
|
</button>
|
|
973
981
|
```
|
|
974
982
|
|
|
975
|
-
Tooltips are **opt-in** (JS init required
|
|
983
|
+
Tooltips are **opt-in** (JS init required; see [JavaScript initialization](#javascript-initialization)). Only attach to focusable elements so keyboard users can trigger them; never put essential information _only_ in a tooltip, and never report form errors through a tooltip. `data-bs-html` with untrusted content is an XSS vector.
|
|
976
984
|
|
|
977
985
|
## JavaScript Initialization
|
|
978
986
|
|
|
@@ -1003,14 +1011,14 @@ Bootstrap's core CSS ships **no icons**. The `.bi` SVGs in examples come from th
|
|
|
1003
1011
|
|
|
1004
1012
|
### Status glyph marks
|
|
1005
1013
|
|
|
1006
|
-
|
|
1014
|
+
For a textless status mark — a dot, tick, ring, or pulse — use a glyph rather than a badge ([Badge](#badge)). Apply the following composition rules to inline SVG and icon fonts:
|
|
1007
1015
|
|
|
1008
1016
|
```html
|
|
1009
1017
|
<span class="bi bi-circle-fill fs-6 lh-1" role="img" aria-label="Healthy"></span>
|
|
1010
1018
|
<span class="bi bi-circle fs-6 lh-1" role="img" aria-label="Not started"></span>
|
|
1011
1019
|
```
|
|
1012
1020
|
|
|
1013
|
-
- **Inherit the owning foreground.** Keep ordinary glyphs on body or component text, including selected fills. Add `text-*-emphasis` only for a deliberate semantic tint on a known, measured surface; never apply it to every mark on a subtle fill. Measure meaningful marks
|
|
1021
|
+
- **Inherit the owning foreground.** Keep ordinary glyphs on body or component text, including selected fills. Add `text-*-emphasis` only for a deliberate semantic tint on a known, measured surface; never apply it to every mark on a subtle fill. Measure meaningful marks against the bar in [SKILL.md](../SKILL.md) → Surfaces, color, contrast, in every declared theme and reached state. Take cascade exceptions from [color-modes.md](color-modes.md).
|
|
1014
1022
|
- **Filled and hollow say different things** — done vs pending, live vs idle — so pair glyphs that share one advance width (a filled/hollow pair from the same icon family). Mixed widths make a column of marks jitter row to row.
|
|
1015
1023
|
- **Size with `fs-*` _and_ `lh-1`.** A glyph inherits the row's line-height, so an `fs-*` bump without `lh-1` grows the line box and pushes the row taller than its neighbors.
|
|
1016
1024
|
- Give the mark an accessible name (`role="img"` + `aria-label`, or a `.visually-hidden` word next to an `aria-hidden` glyph) — a mark whose only meaning is its color and shape is color-only status.
|
|
@@ -1040,7 +1048,7 @@ The textless mark that survives both themes — dots, ticks, rings, pulses — i
|
|
|
1040
1048
|
- Prefer visible labels or `.form-floating` — placeholder-only labels fail accessibility and disappear on input.
|
|
1041
1049
|
- Pair help and errors with `aria-describedby`; use `.invalid-feedback` with `.is-invalid` and mark the field `aria-invalid="true"`.
|
|
1042
1050
|
- Money/units: `.input-group` + `.input-group-text`; add `.has-validation` on groups with validation feedback.
|
|
1043
|
-
-
|
|
1051
|
+
- Keep submit enabled while fields are invalid and validate on submit. Mark the button busy while a submit is in flight — `spinner-border spinner-border-sm` inside it, `aria-busy="true"` on it — and refuse a second submit; take the full rule from [bootstrap-reference.md](bootstrap-reference.md) → Forms in production.
|
|
1044
1052
|
|
|
1045
1053
|
### Selection fills
|
|
1046
1054
|
|
|
@@ -1048,7 +1056,7 @@ Keep the component's selected foreground on ordinary labels and glyphs. Remove a
|
|
|
1048
1056
|
semantic tint before changing its active fill; handle an independently filled badge through
|
|
1049
1057
|
[color-modes.md](color-modes.md) → Alerts, buttons, and selection.
|
|
1050
1058
|
|
|
1051
|
-
Verify that chosen and unchosen filters remain distinguishable in
|
|
1059
|
+
Verify that chosen and unchosen filters remain distinguishable in every declared theme. Do not assume
|
|
1052
1060
|
that a neutral outline always inverts meaning or that an accent hue repairs it. Preserve the
|
|
1053
1061
|
checked/pressed state and add a visible non-color cue when the fill alone is ambiguous.
|
|
1054
1062
|
|
|
@@ -1060,7 +1068,7 @@ to every selection widget.
|
|
|
1060
1068
|
|
|
1061
1069
|
- Active nav items need `aria-current="page"` (or `aria-selected="true"` for tabs).
|
|
1062
1070
|
- Modals and offcanvas: set `aria-labelledby`; Bootstrap traps focus and restores it on close — do not fight it; `dispose()` instances when the host unmounts in SPAs.
|
|
1063
|
-
- Icon-only controls always need an accessible name (`aria-label` or visually-hidden text) and a
|
|
1071
|
+
- Icon-only controls always need an accessible name (`aria-label` or visually-hidden text) and a target meeting the floor in [bootstrap-reference.md](bootstrap-reference.md) → WCAG 2.2 requirements for app UI.
|
|
1064
1072
|
|
|
1065
1073
|
### Theming
|
|
1066
1074
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Frontend Design
|
|
2
2
|
|
|
3
|
-
> Part of the `enterprise-bootstrap`
|
|
4
|
-
> typography, color, depth, imagery,
|
|
3
|
+
> Part of the `enterprise-bootstrap` skill. Aesthetic, hierarchy, spacing,
|
|
4
|
+
> typography, color, depth, imagery, signature, and copy — use when setting visual direction.
|
|
5
|
+
> The pass order lives in [SKILL.md](../SKILL.md) → Process.
|
|
5
6
|
> Operate layer: [SKILL.md](../SKILL.md).
|
|
6
7
|
|
|
7
8
|
Give the surface a point of view rooted in its subject. Make the person's task clear before making
|
|
@@ -15,13 +16,17 @@ content, and its design system before choosing a direction. Use prior designs an
|
|
|
15
16
|
hints, not templates. Draw character from the subject's materials, instruments, artifacts, and
|
|
16
17
|
vernacular rather than an unrelated visual trend.
|
|
17
18
|
|
|
18
|
-
Set personality through
|
|
19
|
-
register
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
Set personality through these levers — typeface, primary color, corner-radius family, and copy
|
|
20
|
+
register. Choose each from the audience and subject, then hold it on every screen. In an existing
|
|
21
|
+
product the levers are already set: carry them forward and move none without a brief that asks for
|
|
22
|
+
it.
|
|
23
|
+
|
|
24
|
+
Pick the typeface and radius against the register the brief names: take a serif for a classic
|
|
25
|
+
register, a rounded sans or a large radius for a playful one, and no radius for a formal one. Take a
|
|
26
|
+
neutral sans-serif and a small radius for a neutral register as a deliberate choice, never as a
|
|
27
|
+
skipped one. Use one radius family per product; pill buttons beside square cards read as two
|
|
28
|
+
products. Take the copy register from what the audience already uses, not from a competitor's
|
|
29
|
+
interface.
|
|
25
30
|
|
|
26
31
|
Start with the smallest useful feature: what the person needs to see, enter, decide, and do next.
|
|
27
32
|
Compose that interaction with realistic content before choosing its navigation shell. Reuse a shell
|
|
@@ -36,7 +41,7 @@ Name the primary information, supporting context, and ancillary detail in each t
|
|
|
36
41
|
that order readable without color: use placement, grouping, weight, and spacing before adding
|
|
37
42
|
paint. When the primary element does not stand out, quiet its competitors before enlarging it.
|
|
38
43
|
|
|
39
|
-
Carry hierarchy with
|
|
44
|
+
Carry hierarchy with a short set of foreground tiers and a weight pair before reaching for size;
|
|
40
45
|
size alone produces oversized primary text and unreadable secondary text. Use a regular body
|
|
41
46
|
weight (400) and one emphasis weight (600–700); weights under 400 belong only at display sizes.
|
|
42
47
|
Quiet a heavy element by lowering its contrast — an icon beside a label takes the secondary tier
|
|
@@ -105,14 +110,15 @@ to make it fit.
|
|
|
105
110
|
Assign display, body, and utility roles; they need not be different font families. Reuse the
|
|
106
111
|
product's typefaces. For a new system, take a legible UI face for repeated reading and data, and add
|
|
107
112
|
a display face only where its character earns the payload. A neutral sans-serif or system stack is
|
|
108
|
-
a deliberate choice, not a failure of distinctiveness. Prefer
|
|
109
|
-
|
|
113
|
+
a deliberate choice, not a failure of distinctiveness. Prefer a family shipping the `300`–`700`
|
|
114
|
+
range so display, body, and emphasis roles draw from one family; avoid condensed or
|
|
115
|
+
short-x-height faces for UI text; keep a display face at display size,
|
|
110
116
|
where it was drawn to work. Test the actual glyphs, numerals, weights, languages, and fallback the
|
|
111
117
|
surface needs.
|
|
112
118
|
|
|
113
119
|
Hand-pick a finite type scale in `rem`, with smaller jumps for UI text and larger jumps for
|
|
114
120
|
display; a modular ratio yields fractional pixels and too few reading sizes. Avoid nested `em`
|
|
115
|
-
font sizes that compound off-scale. Start with
|
|
121
|
+
font sizes that compound off-scale. Start with a regular weight and an emphasis weight, and
|
|
116
122
|
add another only for a distinct role. Keep captions and metadata at the smallest readable step in
|
|
117
123
|
the secondary tier, not smaller in body color. Never shrink text merely to avoid fixing a cramped
|
|
118
124
|
layout.
|
|
@@ -140,10 +146,11 @@ on touch or keyboard.
|
|
|
140
146
|
|
|
141
147
|
### Color as a constrained system
|
|
142
148
|
|
|
143
|
-
Define roles, not a handful of unrelated swatches. Reuse or establish a neutral ramp,
|
|
144
|
-
|
|
145
|
-
ramp
|
|
146
|
-
and interaction states.
|
|
149
|
+
Define roles, not a handful of unrelated swatches. Reuse or establish a neutral ramp, the brand
|
|
150
|
+
families the identity needs, and only the status or categorical families the feature needs. Build
|
|
151
|
+
the neutral ramp across the shipped `100…900` steps, and give each brand and status family enough
|
|
152
|
+
steps for text, borders, fills, and interaction states. Declare a step because a role consumes it,
|
|
153
|
+
never to fill the ramp.
|
|
147
154
|
|
|
148
155
|
Choose a family's base in a real control, its dark edge in text, and its light edge in a subtle
|
|
149
156
|
surface; fill the gaps with visibly distinct steps, middle first. Define every shade up front;
|
|
@@ -167,7 +174,7 @@ the color without the weight of a dark fill, and reserves the solid pair for the
|
|
|
167
174
|
element. Reserve explicit foreground/background pairs for intentional solid, inverse, or
|
|
168
175
|
image-backed regions. Keep a secondary foreground within that region's tested contract; do not apply neutral
|
|
169
176
|
grey or reduced opacity by habit. When no quieter foreground passes, separate by weight or spacing.
|
|
170
|
-
Review hierarchy in
|
|
177
|
+
Review hierarchy in every declared mode independently. Preserve relative prominence and useful
|
|
171
178
|
separation rather than mechanically inverting shades or adding a border to every dark panel.
|
|
172
179
|
|
|
173
180
|
Use color to reinforce a word, glyph, position, or pattern, never to carry meaning alone. Give
|
|
@@ -196,9 +203,10 @@ Give overlapping images a ring in the background color so they never clash. Keep
|
|
|
196
203
|
weights in one family.
|
|
197
204
|
|
|
198
205
|
Spend polish on the content already present before adding another accessory — icon bullets that mean something, a brand-colored check, a
|
|
199
|
-
promoted quotation mark, a link underline that completes on hover.
|
|
200
|
-
— top of a card, side of a callout, under a heading or the active nav item
|
|
201
|
-
|
|
206
|
+
promoted quotation mark, a link underline that completes on hover. Use one accent border per region
|
|
207
|
+
— top of a card, side of a callout, under a heading, or the active nav item. Repeating that accent
|
|
208
|
+
across neighboring regions turns it into a pattern and it stops reading as an accent. Change a
|
|
209
|
+
section's surface before decorating it; keep
|
|
202
210
|
any gradient within about 30° of hue and any pattern low-contrast and away from text. An accent,
|
|
203
211
|
pattern, or background treatment supports grouping, state, or the subject; never add one to
|
|
204
212
|
compensate for weak hierarchy. Take the class recipes from [utilities.md](utilities.md) →
|
|
@@ -253,36 +261,23 @@ matters. Give richer components richer content without replacing their semantics
|
|
|
253
261
|
in a menu, related non-comparable details in one table cell, native radios inside selectable cards.
|
|
254
262
|
Preserve keyboard behavior, sorting, and the comparisons the task depends on.
|
|
255
263
|
|
|
256
|
-
##
|
|
264
|
+
## Hold the direction against defaults
|
|
265
|
+
|
|
266
|
+
Run the pass order in [SKILL.md](../SKILL.md) → Process. That section owns the sequence; this file
|
|
267
|
+
owns the visual decisions each pass makes.
|
|
257
268
|
|
|
258
269
|
Calibrate against recurring defaults: cream with serif and terracotta; near-black with
|
|
259
270
|
acid green or vermilion; broadsheet hairlines, square corners, and dense columns. These can fit a
|
|
260
271
|
brief; they are not evidence that a direction fits this one. Follow a pinned direction exactly and
|
|
261
272
|
use free axes deliberately, not as an excuse to rebrand an existing product.
|
|
262
273
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
a workable arrangement.
|
|
271
|
-
3. **Plan the system.** Name palette families and surface ownership; type roles, sizes, and weights; spacing
|
|
272
|
-
and width roles; radius and elevation; the signature the brief calls for or the existing identity to preserve. Reuse existing
|
|
273
|
-
tokens and record only the additions or changes. State the layout in prose or a small wireframe.
|
|
274
|
-
4. **Critique, then build.** Reject a plan that obscures the task, implies unbuilt behavior, or reads
|
|
275
|
-
as interchangeable. Implement the smallest working flow, map shared tokens at their owning scope, and refine its states
|
|
276
|
-
before extending the next feature. Revise the shared plan when the render disproves it.
|
|
277
|
-
5. **Critique the render.** Read task, hierarchy, grouping, type, contrast, state, and signature in
|
|
278
|
-
that order. Fix the earliest failure before decorating later layers. Recheck after the fix.
|
|
279
|
-
|
|
280
|
-
Keep CSS specificity deliberate. Utilities may use `!important`; inspect the winning declaration
|
|
281
|
-
instead of stacking overrides. Remove conflicting classes before escalating selector specificity.
|
|
282
|
-
A class selector remains a class selector regardless of its name.
|
|
283
|
-
|
|
284
|
-
Keep exploratory drafts private. Show the selected direction, relevant decisions, and verification
|
|
285
|
-
limits, not every discarded variation. Do not claim a rendered result from source inspection.
|
|
274
|
+
Reject a direction that obscures the task, implies unbuilt behavior, or reads as interchangeable
|
|
275
|
+
with any other product. Keep exploratory drafts private: show the selected direction, the decisions
|
|
276
|
+
behind it, and the verification limits, not every discarded variation. Never claim a rendered result
|
|
277
|
+
from source inspection.
|
|
278
|
+
|
|
279
|
+
Keep specificity deliberate through [SKILL.md](../SKILL.md) → The styling ladder, which owns that
|
|
280
|
+
rule.
|
|
286
281
|
|
|
287
282
|
## Restraint and self-critique
|
|
288
283
|
|
|
@@ -302,7 +297,7 @@ Review captures at the declared widths, themes, and states, with keyboard focus
|
|
|
302
297
|
content. Switch modes on the mounted interface; inspect inherited text, quiet fills, and selected
|
|
303
298
|
controls before polishing decoration. Compare against the named design criteria; do not invent a numeric beauty score. Use
|
|
304
299
|
[inspection.md](inspection.md) for measurable claims and its rendered design review for visual
|
|
305
|
-
ones. Record untested coverage as open.
|
|
300
|
+
ones. Record untested coverage as open.
|
|
306
301
|
|
|
307
302
|
## Writing in design
|
|
308
303
|
|