baseline-foundry 0.1.6 → 0.2.0

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.
Files changed (80) hide show
  1. package/README.md +22 -7
  2. package/config/canonical-spacing.resolved.json +71 -0
  3. package/config/experiments/ibm-plex-engine-smoke.json +7 -6
  4. package/config/experiments/ubuntu-engine-smoke.json +7 -6
  5. package/config/foundation-theme.json +7 -6
  6. package/config/tiers/app.json +8 -9
  7. package/config/tiers/documentation.json +7 -8
  8. package/config/tiers/editorial.json +7 -8
  9. package/config/tiers/os.json +7 -8
  10. package/dist/application-layout.js +1 -1
  11. package/dist/build.d.ts +1 -0
  12. package/dist/build.js +98 -36
  13. package/dist/css-app-tier.js +4 -46
  14. package/dist/css-component-contracts.d.ts +7 -0
  15. package/dist/css-component-contracts.js +82 -0
  16. package/dist/css-components/application-geometry.d.ts +5 -0
  17. package/dist/css-components/application-geometry.js +31 -0
  18. package/dist/css-components/article-pagination.js +6 -5
  19. package/dist/css-components/button-actions.js +68 -4
  20. package/dist/css-components/cards-options.js +2 -2
  21. package/dist/css-components/chip-badge-status.d.ts +0 -1
  22. package/dist/css-components/chip-badge-status.js +37 -15
  23. package/dist/css-components/content-card.js +11 -10
  24. package/dist/css-components/control-geometry.d.ts +12 -0
  25. package/dist/css-components/control-geometry.js +22 -0
  26. package/dist/css-components/cta-figure-aspect.js +2 -2
  27. package/dist/css-components/document-navigation.js +28 -18
  28. package/dist/css-components/editorial-content.js +1 -1
  29. package/dist/css-components/icon.js +24 -2
  30. package/dist/css-components/interactive-feedback.js +27 -14
  31. package/dist/css-components/interactive-tables.js +9 -6
  32. package/dist/css-components/legacy-navigation.js +32 -25
  33. package/dist/css-components/list-tree.js +5 -5
  34. package/dist/css-components/list.js +17 -12
  35. package/dist/css-components/logo-media.js +2 -2
  36. package/dist/css-components/navigation-geometry.d.ts +5 -0
  37. package/dist/css-components/navigation-geometry.js +30 -0
  38. package/dist/css-components/nested-controls.d.ts +8 -0
  39. package/dist/css-components/nested-controls.js +37 -0
  40. package/dist/css-components/panel.js +12 -13
  41. package/dist/css-components/search-box-and-filter.d.ts +0 -1
  42. package/dist/css-components/search-box-and-filter.js +3 -3
  43. package/dist/css-components/sites-editorial-ports.js +8 -1
  44. package/dist/css-components/sites-foundation.js +2 -2
  45. package/dist/css-components/table.js +9 -1
  46. package/dist/css-components/tabs-choice-breadcrumbs.js +9 -9
  47. package/dist/css-components/tiered-list-equal-height-row.js +2 -2
  48. package/dist/css-components.js +99 -229
  49. package/dist/css.js +44 -1
  50. package/dist/dtcg-spacing.d.ts +40 -0
  51. package/dist/dtcg-spacing.js +163 -0
  52. package/dist/experiments/ibm-plex-engine-smoke/styles.css +557 -360
  53. package/dist/experiments/ibm-plex-engine-smoke/surfaces.json +182 -14
  54. package/dist/experiments/ibm-plex-engine-smoke/tokens.json +91 -7
  55. package/dist/index.d.ts +1 -0
  56. package/dist/presets/app-tier/styles.css +700 -494
  57. package/dist/presets/app-tier/surfaces.json +709 -29
  58. package/dist/presets/app-tier/tokens.json +178 -8
  59. package/dist/presets/prose/styles.css +699 -486
  60. package/dist/presets/prose/surfaces.json +709 -29
  61. package/dist/presets/prose/tokens.json +177 -7
  62. package/dist/styles.css +699 -486
  63. package/dist/surfaces.json +709 -29
  64. package/dist/tiers/app/styles.css +700 -494
  65. package/dist/tiers/app/surfaces.json +709 -29
  66. package/dist/tiers/app/tokens.json +178 -8
  67. package/dist/tiers/documentation/styles.css +699 -486
  68. package/dist/tiers/documentation/surfaces.json +709 -29
  69. package/dist/tiers/documentation/tokens.json +177 -7
  70. package/dist/tiers/editorial/styles.css +699 -486
  71. package/dist/tiers/editorial/surfaces.json +709 -29
  72. package/dist/tiers/editorial/tokens.json +177 -7
  73. package/dist/tiers/os/styles.css +699 -486
  74. package/dist/tiers/os/surfaces.json +709 -29
  75. package/dist/tiers/os/tokens.json +177 -7
  76. package/dist/tokens.json +177 -7
  77. package/dist/types.d.ts +13 -13
  78. package/dist/vanilla-theme-colors.js +5 -1
  79. package/docs/publishing.md +7 -1
  80. package/package.json +1 -1
@@ -1,34 +1,12 @@
1
1
  export function appTierPresetCss(scopes) {
2
2
  const scoped = (suffix = "") => scopes.map(scope => `${scope}${suffix}`).join(",\n");
3
- return `/* Canonical app-tier preset: light application chrome, Ubuntu Sans, and app-surface control overrides. */
3
+ return `/* Canonical app-tier preset: application chrome only. Component
4
+ typography and spacing resolve from the tier inputs in their source modules. */
4
5
  ${scoped()} {
5
6
  --bf-app-demo-page-bg: var(--vf-color-background-alt, #f7f7f7);
6
- --bf-radius: 0;
7
7
  color-scheme: light;
8
8
  }
9
9
 
10
- ${scoped(" :where(.bf-form-label, .bf-form-help, .bf-button, .bf-button.is-base, .bf-status-label, .bf-chip, .bf-checkbox-label, .bf-radio-label, .bf-tabs-link, .bf-accordion-tab, .bf-validation-message)")} {
11
- font-family: inherit;
12
- font-size: var(--bf-body-font-size);
13
- font-style: normal;
14
- font-weight: 400;
15
- line-height: var(--bf-body-line-height);
16
- }
17
-
18
- /* Preserve the public nested fit after the preset's general typography pass.
19
- The modifier remains explicit and zero-specificity in every app scope. */
20
- ${scoped(" :where(.bf-status-label.is-nested, .bf-chip.is-nested)")} {
21
- line-height: var(--bf-nested-auxiliary-line-height);
22
- }
23
-
24
- ${scoped(" :where(.bf-input, .bf-button, .bf-button.is-base)")} {
25
- font-family: inherit;
26
- font-size: var(--bf-body-font-size);
27
- font-style: normal;
28
- font-weight: 400;
29
- line-height: var(--bf-body-line-height);
30
- }
31
-
32
10
  ${scoped(" :where(.bf-application)")} {
33
11
  background: var(--bf-app-demo-page-bg);
34
12
  }
@@ -39,13 +17,6 @@ ${scoped(" :where(.bf-aside.is-overlay, .bf-aside.is-drawer)")} {
39
17
  box-shadow: 0 0.625rem 1.25rem rgba(0, 0, 0, 0.12), 0 0 0.1875rem rgba(0, 0, 0, 0.12);
40
18
  }
41
19
 
42
- ${scoped(" :where(.bf-panel-header)")} {
43
- align-items: start;
44
- gap: calc(var(--bf-baseline) * 1.5);
45
- padding-block-end: var(--bf-panel-padding-block);
46
- padding-block-start: var(--bf-panel-padding-block);
47
- }
48
-
49
20
  ${scoped(" :where(.bf-panel-header.is-navigation-brand)")} {
50
21
  gap: 0;
51
22
  padding-block: 0;
@@ -56,22 +27,10 @@ ${scoped(" :where(.bf-navigation-bar) :where(.bf-panel-header)")} {
56
27
  }
57
28
 
58
29
  ${scoped(" :where(.bf-navigation-bar.is-responsive) :where(.bf-panel-header.is-navigation-brand)")} {
59
- gap: calc(var(--bf-baseline) * 2);
30
+ gap: var(--bf-leading-mark-gap);
60
31
  padding-block: 0;
61
32
  }
62
33
 
63
- ${scoped(" :where(.bf-panel-controls)")} {
64
- gap: calc(var(--bf-baseline) * 1.5);
65
- }
66
-
67
- ${scoped(" :where(.bf-navigation-bar, .bf-navigation-drawer, .bf-aside) :where(.bf-panel-title)")} {
68
- font-family: inherit;
69
- font-size: 1rem;
70
- font-style: normal;
71
- font-weight: 400;
72
- line-height: 1.5rem;
73
- }
74
-
75
34
  ${scoped(" :where(.bf-side-navigation-link.is-active, .bf-side-navigation-link[aria-current='page'], .bf-side-navigation-link[aria-current='true'])")} {
76
35
  background: transparent;
77
36
  box-shadow: inset var(--bf-bar-thickness) 0 0 var(--vf-color-link-default, #0066cc);
@@ -79,8 +38,7 @@ ${scoped(" :where(.bf-side-navigation-link.is-active, .bf-side-navigation-link[a
79
38
 
80
39
  ${scoped(" :where(.bf-navigation-bar)")},
81
40
  ${scoped(" :where(.bf-navigation-drawer)")},
82
- ${scoped(" :where(.bf-aside)")},
83
- ${scoped(" :where(.bf-panel)")} {
41
+ ${scoped(" :where(.bf-aside)")} {
84
42
  background: var(--vf-color-background-default, #ffffff);
85
43
  }
86
44
 
@@ -0,0 +1,7 @@
1
+ import type { ThemeSurface, ThemeTokens } from "./types.js";
2
+ /**
3
+ * Shared component spacing contracts. Tier scopes provide facts; every
4
+ * derived value below remains expressed in those facts so class-scoped and
5
+ * direct tier bundles use identical geometry.
6
+ */
7
+ export declare function componentContractsCss(tokens: ThemeTokens, themeSurfaces?: ThemeSurface[]): string;
@@ -0,0 +1,82 @@
1
+ function componentInputDeclarations(components) {
2
+ return ` --bf-border-width: ${components.borderWidth};
3
+ --bf-bar-thickness: ${components.barThickness};
4
+ --bf-radius: ${components.radius};
5
+ --bf-component-inline-inset-action-bordered: max(0rem, calc(var(--bf-component-inline-inset-action) - var(--bf-border-width)));
6
+ --bf-control-visual-size: ${components.controlVisualSize};
7
+ `;
8
+ }
9
+ /**
10
+ * Shared component spacing contracts. Tier scopes provide facts; every
11
+ * derived value below remains expressed in those facts so class-scoped and
12
+ * direct tier bundles use identical geometry.
13
+ */
14
+ export function componentContractsCss(tokens, themeSurfaces = []) {
15
+ const body = tokens.roles.body;
16
+ const bodyLineHeight = `var(--bf-body-line-height, ${body.lineHeight})`;
17
+ const bodyNudgeStart = `var(--bf-body-nudge-start, ${body.nudgeTop})`;
18
+ return `:where(.bf-theme) {
19
+ ${componentInputDeclarations(tokens.components)} /* Three authoritative component content insets. Page margins, grid
20
+ gutters, surface placement and navigation depth remain structural. */
21
+
22
+ /* Regular single-line interface rows use one metric-derived occupied block.
23
+ Visible and transparent block borders both consume the frame slots. */
24
+ --bf-interface-row-line-height: ${bodyLineHeight};
25
+ --bf-interface-row-padding-block: max(0rem, calc(${bodyNudgeStart} - var(--bf-border-width)));
26
+ --bf-interface-row-painted-block-size: calc(var(--bf-interface-row-line-height) + (var(--bf-interface-row-padding-block) * 2) + (var(--bf-border-width) * 2));
27
+ --bf-interface-row-compensation-block-end: mod(calc(var(--bf-baseline) - mod(var(--bf-interface-row-painted-block-size), var(--bf-baseline))), var(--bf-baseline));
28
+ --bf-interface-row-occupied-block-size: calc(var(--bf-interface-row-painted-block-size) + var(--bf-interface-row-compensation-block-end));
29
+ --bf-interface-row-content-offset-block-start: calc(var(--bf-border-width) + var(--bf-interface-row-padding-block));
30
+ --bf-interface-row-visual-offset: calc(var(--bf-interface-row-content-offset-block-start) + ((var(--bf-interface-row-line-height) - var(--bf-control-visual-size)) / 2));
31
+
32
+ /* Host-owned rows keep the same occupied target but absorb compensation
33
+ inside the box. A ruled host subtracts its separator in its own module. */
34
+ --bf-in-box-row-padding-block-start: var(--bf-interface-row-content-offset-block-start);
35
+ --bf-in-box-row-padding-block-end: max(0rem, calc(var(--bf-interface-row-occupied-block-size) - var(--bf-interface-row-line-height) - var(--bf-in-box-row-padding-block-start)));
36
+
37
+ /* Nested children fit inside the host body line and contribute no external
38
+ compensation. Zero-footprint surfaces and real two-border controls use
39
+ the body line minus one baseline; build validation proves that this line
40
+ can contain the font, visual and framed ledgers for every tier. */
41
+ --bf-nested-row-line-height: calc(var(--bf-interface-row-line-height) - var(--bf-baseline));
42
+ --bf-nested-row-padding-block: max(0rem, calc((var(--bf-interface-row-line-height) - var(--bf-nested-row-line-height)) / 2));
43
+ --bf-nested-row-painted-block-size: calc(var(--bf-nested-row-line-height) + (var(--bf-nested-row-padding-block) * 2));
44
+ --bf-nested-framed-row-padding-block: max(0rem, calc((var(--bf-interface-row-line-height) - var(--bf-nested-row-line-height) - (var(--bf-border-width) * 2)) / 2));
45
+ --bf-nested-framed-row-painted-block-size: calc(var(--bf-nested-row-line-height) + (var(--bf-nested-framed-row-padding-block) * 2) + (var(--bf-border-width) * 2));
46
+ --bf-nested-framed-row-visual-offset: calc(var(--bf-border-width) + var(--bf-nested-framed-row-padding-block) + ((var(--bf-nested-row-line-height) - var(--bf-control-visual-size)) / 2));
47
+
48
+ /* Block-derived members consume one inline-minimum name. The default is
49
+ the regular framed paint; selectors below re-point the name to what each
50
+ member actually paints, rather than to the row ledger it occupies. */
51
+ --bf-square-block-size: var(--bf-interface-row-painted-block-size);
52
+
53
+ /* Leading marks are positioned backwards from the continuation copy inset;
54
+ a mark never creates a fourth content inset. */
55
+ --bf-leading-mark-size: var(--bf-control-visual-size);
56
+ --bf-leading-mark-offset: calc(var(--bf-leading-mark-size) + var(--bf-leading-mark-gap));
57
+ --bf-leading-mark-group-inset: calc(var(--bf-component-inline-inset-continuation) - var(--bf-leading-mark-offset));
58
+ --bf-tick-label-offset: var(--bf-leading-mark-offset);
59
+ }
60
+
61
+ :where(.bf-theme) :where(.bf-badge) {
62
+ --bf-square-block-size: var(--bf-interface-row-line-height);
63
+ }
64
+
65
+ :where(.bf-theme) :where(.bf-badge.is-nested) {
66
+ --bf-square-block-size: var(--bf-nested-row-line-height);
67
+ }
68
+
69
+ :where(.bf-theme) :where(.bf-button.is-link.is-icon:not(.is-nested):not(:has(.bf-button-label))) {
70
+ --bf-square-block-size: var(--bf-body-line-height);
71
+ }
72
+
73
+ :where(.bf-theme) :where(.bf-notification-close) {
74
+ --bf-square-block-size: var(--bf-notification-close-painted-block-size);
75
+ }
76
+
77
+ ${themeSurfaces
78
+ .filter(surface => surface.className)
79
+ .map(surface => `:where(.bf-theme.${surface.className}) {\n${componentInputDeclarations(surface.tokens.components)}}`)
80
+ .join("\n\n")}
81
+ `;
82
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Application-shell dimensions. These are shell facts, not component inset
3
+ * or occupied-row contracts; colors remain owned by the theme color map.
4
+ */
5
+ export declare function applicationGeometryCss(): string;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Application-shell dimensions. These are shell facts, not component inset
3
+ * or occupied-row contracts; colors remain owned by the theme color map.
4
+ */
5
+ export function applicationGeometryCss() {
6
+ return `:where(.bf-theme) {
7
+ --bf-app-drawer-width-icon: 2rem;
8
+ --bf-app-drawer-width-small: 15rem;
9
+ --bf-app-drawer-width-small-max: 20rem;
10
+ --bf-app-drawer-width-medium: 29.0625rem;
11
+ --bf-app-drawer-width-medium-max: 40rem;
12
+ --bf-app-drawer-width-large: min(100vw, max(40rem, 50vw));
13
+ --bf-app-aside-width: var(--bf-app-drawer-width-medium);
14
+ --bf-app-aside-width-min: var(--bf-app-drawer-width-small);
15
+ --bf-app-aside-width-max: var(--bf-app-drawer-width-medium-max);
16
+ --bf-app-navigation-width: 15rem;
17
+ --bf-app-navigation-width-collapsed: 3rem;
18
+ --bf-application-drawer-width-icon: var(--bf-app-drawer-width-icon);
19
+ --bf-application-drawer-width-small: var(--bf-app-drawer-width-small);
20
+ --bf-application-drawer-width-small-max: var(--bf-app-drawer-width-small-max);
21
+ --bf-application-drawer-width-medium: var(--bf-app-drawer-width-medium);
22
+ --bf-application-drawer-width-medium-max: var(--bf-app-drawer-width-medium-max);
23
+ --bf-application-drawer-width-large: var(--bf-app-drawer-width-large);
24
+ --bf-application-aside-width: var(--bf-app-aside-width);
25
+ --bf-application-aside-width-min: var(--bf-app-aside-width-min);
26
+ --bf-application-aside-width-max: var(--bf-app-aside-width-max);
27
+ --bf-application-navigation-width: var(--bf-app-navigation-width);
28
+ --bf-application-navigation-width-collapsed: var(--bf-app-navigation-width-collapsed);
29
+ }
30
+ `;
31
+ }
@@ -22,9 +22,9 @@ export function articlePaginationCss(options) {
22
22
  box-sizing: border-box;
23
23
  color: var(--bf-color-text-default);
24
24
  display: grid;
25
- column-gap: var(--bf-space-2);
25
+ column-gap: var(--bf-leading-mark-gap);
26
26
  grid-template-rows: auto auto;
27
- inline-size: calc((100cqi - var(--bf-space-2)) / 2);
27
+ inline-size: calc((100cqi - var(--bf-leading-mark-gap)) / 2);
28
28
  min-inline-size: 0;
29
29
  padding-block: calc(var(--bf-space-2) + (var(--bf-baseline) / 4) - var(--bf-border-width));
30
30
  position: relative;
@@ -49,7 +49,7 @@ export function articlePaginationCss(options) {
49
49
  :where(.bf-theme) :where(.bf-article-pagination-link.is-previous) {
50
50
  grid-column: 1;
51
51
  grid-template-columns: var(--bf-icon-size-default) minmax(0, 1fr);
52
- padding-inline: var(--bf-space-1) var(--bf-space-2);
52
+ padding-inline: var(--bf-leading-mark-gap) var(--bf-component-inline-inset-action);
53
53
  text-align: start;
54
54
  }
55
55
 
@@ -57,7 +57,7 @@ export function articlePaginationCss(options) {
57
57
  grid-column: 2;
58
58
  grid-template-columns: minmax(0, 1fr) var(--bf-icon-size-default);
59
59
  justify-self: end;
60
- padding-inline: var(--bf-space-2) var(--bf-space-1);
60
+ padding-inline: var(--bf-component-inline-inset-action) var(--bf-leading-mark-gap);
61
61
  text-align: end;
62
62
  }
63
63
 
@@ -94,6 +94,7 @@ ${bodyCaseTypeStyles} color: inherit;
94
94
 
95
95
  :where(.bf-theme) :where(.bf-article-pagination-direction > .bf-icon) {
96
96
  --bf-icon-size: var(--bf-icon-size-default);
97
+ margin-block-start: 0;
97
98
  }
98
99
 
99
100
  :where(.bf-theme) .bf-article-pagination:dir(rtl) .bf-article-pagination-link.is-previous .bf-icon {
@@ -126,7 +127,7 @@ ${titleTypeStyles} color: inherit;
126
127
  :where(.bf-theme) :where(.bf-article-pagination-link.is-previous:not(:only-child)) {
127
128
  grid-template-columns: var(--bf-icon-size-default);
128
129
  inline-size: calc(var(--bf-space-6) + var(--bf-space-1));
129
- padding-inline: var(--bf-space-1);
130
+ padding-inline: var(--bf-leading-mark-gap);
130
131
  }
131
132
 
132
133
  :where(.bf-theme) :where(.bf-article-pagination-link.is-next:not(:only-child)) {
@@ -9,7 +9,7 @@ ${bodyTypeStyles} appearance: none;
9
9
  cursor: pointer;
10
10
  display: inline-block;
11
11
  margin-bottom: ${buttonMarginBottom};
12
- ${buttonPadding} padding-inline: var(--bf-control-inline-padding-action-bordered);
12
+ ${buttonPadding} padding-inline: var(--bf-component-inline-inset-action-bordered);
13
13
  text-align: center;
14
14
  text-decoration: none;
15
15
  }
@@ -138,7 +138,7 @@ ${buttonPadding} padding-inline: var(--bf-control-inline-padding-action-bordere
138
138
 
139
139
  :where(.bf-theme) :where(.bf-button.is-icon) {
140
140
  align-items: center;
141
- column-gap: var(--bf-space-1);
141
+ column-gap: var(--bf-leading-mark-gap);
142
142
  display: inline-flex;
143
143
  justify-content: center;
144
144
  }
@@ -146,21 +146,44 @@ ${buttonPadding} padding-inline: var(--bf-control-inline-padding-action-bordere
146
146
  /* An icon-only flex button has no text line box to preserve the occupied
147
147
  * control rhythm. A zero-width metric strut restores the active body line
148
148
  * without imposing a target block size or changing icon/label spacing. */
149
- :where(.bf-theme) :where(.bf-button.is-icon:not(:has(.bf-button-label))) {
149
+ :where(.bf-theme) :where(.bf-button.is-icon:not(.is-nested):not(:has(.bf-button-label))) {
150
+ --bf-action-target-overflow: max(0rem, calc((24px - var(--bf-square-block-size)) / 2));
151
+
150
152
  column-gap: 0;
153
+ justify-self: start;
154
+ margin-inline: var(--bf-action-target-overflow);
155
+ min-inline-size: var(--bf-square-block-size);
156
+ padding-inline: 0;
157
+ position: relative;
151
158
  }
152
159
 
153
- :where(.bf-theme) :where(.bf-button.is-icon:not(:has(.bf-button-label)))::before {
160
+ :where(.bf-theme) :where(.bf-button.is-icon:not(.is-nested):not(:has(.bf-button-label)))::before {
154
161
  block-size: var(--bf-body-line-height);
155
162
  content: "";
156
163
  inline-size: 0;
157
164
  }
158
165
 
166
+ /* WCAG 2.2 SC 2.5.8 defines its minimum in CSS pixels. This transparent,
167
+ * out-of-flow box extends only the pointer target; it does not change the
168
+ * control's paint or occupied block geometry. */
169
+ :where(.bf-theme) :where(.bf-button.is-icon:not(.is-nested):not(:has(.bf-button-label)))::after {
170
+ block-size: max(100%, 24px);
171
+ content: "";
172
+ inline-size: max(100%, 24px);
173
+ left: 50%;
174
+ pointer-events: auto;
175
+ position: absolute;
176
+ top: 50%;
177
+ translate: -50% -50%;
178
+ }
179
+
159
180
  :where(.bf-theme) :where(.bf-button-label) {
160
181
  min-inline-size: 0;
161
182
  }
162
183
 
163
184
  :where(.bf-theme) :where(.bf-actions) {
185
+ --bf-action-target-row-gap-floor: var(--bf-baseline);
186
+
164
187
  align-items: center;
165
188
  display: flex;
166
189
  flex-wrap: wrap;
@@ -168,6 +191,19 @@ ${buttonPadding} padding-inline: var(--bf-control-inline-padding-action-bordere
168
191
  min-inline-size: 0;
169
192
  }
170
193
 
194
+ /* Row-gap has no single-line cost and cannot move a flex item's paint. BF's
195
+ * wrapping primitives therefore own a positive block-axis target separation
196
+ * without inspecting their descendants through :has(). */
197
+ :where(.bf-theme) :where(.bf-actions:not(.is-nowrap)) {
198
+ row-gap: max(var(--bf-field-gap), var(--bf-action-target-row-gap-floor));
199
+ }
200
+
201
+ :where(.bf-theme) :where(.bf-cluster:not(.is-nowrap)) {
202
+ --bf-action-target-row-gap-floor: var(--bf-baseline);
203
+
204
+ row-gap: max(var(--bf-cluster-space), var(--bf-action-target-row-gap-floor));
205
+ }
206
+
171
207
  :where(.bf-theme) :where(.bf-actions.is-end) {
172
208
  justify-content: flex-end;
173
209
  }
@@ -178,6 +214,34 @@ ${buttonPadding} padding-inline: var(--bf-control-inline-padding-action-bordere
178
214
  scrollbar-width: thin;
179
215
  }
180
216
 
217
+ /* A nowrap row becomes a clipping scrollport. Only direct icon-only targets
218
+ * reserve their own block overflow, so text-only strips keep their original
219
+ * block size and leading keyline. Existing target-owned inline margins supply
220
+ * the corresponding logical-edge scroll extent. */
221
+ :where(.bf-theme) :where(.bf-actions.is-nowrap) > :where(.bf-button.is-icon:not(.is-nested):not(:has(.bf-button-label))) {
222
+ --bf-action-target-block-clearance: var(--bf-baseline);
223
+
224
+ margin-block-end: calc(var(--bf-action-target-block-clearance) + ${buttonMarginBottom});
225
+ margin-block-start: var(--bf-action-target-block-clearance);
226
+ }
227
+
228
+ :where(.bf-theme) :where(.bf-actions.is-nowrap) > :where(.bf-button.is-link.is-icon:not(.is-nested):not(:has(.bf-button-label))) {
229
+ margin-block-end: var(--bf-action-target-block-clearance);
230
+ }
231
+
232
+ /* Modern CSS rounds the exact inter-row and per-edge scrollport shortfalls up
233
+ * to the active baseline. The one-baseline fallback is safe for built-in tiers
234
+ * and remains on phase in older engines. */
235
+ @supports (row-gap: round(up, 0.0625rem, 0.0625rem)) {
236
+ :where(.bf-theme) :where(.bf-actions:not(.is-nowrap), .bf-cluster:not(.is-nowrap)) {
237
+ --bf-action-target-row-gap-floor: round(up, max(0rem, calc(24px - var(--bf-body-line-height) + var(--bf-border-width))), var(--bf-baseline));
238
+ }
239
+
240
+ :where(.bf-theme) :where(.bf-actions.is-nowrap) > :where(.bf-button.is-icon:not(.is-nested):not(:has(.bf-button-label))) {
241
+ --bf-action-target-block-clearance: round(up, max(0rem, calc((24px - var(--bf-body-line-height)) / 2)), var(--bf-baseline));
242
+ }
243
+ }
244
+
181
245
  :where(.bf-theme) :where(.bf-actions.is-nowrap) > * {
182
246
  flex: 0 0 auto;
183
247
  }
@@ -115,7 +115,7 @@ export function cardsOptionsCss(options) {
115
115
 
116
116
  :where(.bf-theme) :where(.bf-option-grid) {
117
117
  display: grid;
118
- gap: calc(var(--bf-baseline) * 2.5);
118
+ gap: var(--bf-component-inline-inset-action);
119
119
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
120
120
  }
121
121
 
@@ -128,7 +128,7 @@ export function cardsOptionsCss(options) {
128
128
  display: grid;
129
129
  gap: var(--bf-field-gap);
130
130
  margin: 0;
131
- min-block-size: calc((var(--bf-control-box-size) * 2) + var(--bf-baseline));
131
+ min-block-size: calc((var(--bf-interface-row-occupied-block-size) * 2) + var(--bf-baseline));
132
132
  min-inline-size: 0;
133
133
  padding-block-end: calc(var(--bf-panel-padding-block) - var(--bf-border-width));
134
134
  padding-block-start: calc(var(--bf-panel-padding-block) - var(--bf-border-width));
@@ -1,6 +1,5 @@
1
1
  type ChipBadgeStatusCssOptions = {
2
2
  bodyCaseTypeStyles: string;
3
- bodyLineHeight: string;
4
3
  bodyTypeStyles: string;
5
4
  };
6
5
  export declare function chipBadgeStatusCss(options: ChipBadgeStatusCssOptions): string;
@@ -1,6 +1,13 @@
1
1
  export function chipBadgeStatusCss(options) {
2
- const { bodyCaseTypeStyles, bodyLineHeight, bodyTypeStyles, } = options;
3
- return `:where(.bf-theme) :where(.bf-chip, .bf-chip.is-positive, .bf-chip.is-caution, .bf-chip.is-negative, .bf-chip.is-information) {
2
+ const { bodyCaseTypeStyles, bodyTypeStyles, } = options;
3
+ return `:where(.bf-theme) {
4
+ --bf-ui-chip-padding-inline: var(--bf-component-inline-inset-action);
5
+ --bf-ui-chip-radius: 999rem;
6
+ --bf-ui-badge-padding-inline: var(--bf-border-width);
7
+ --bf-ui-badge-overhang: calc(var(--bf-ui-badge-padding-inline) * -0.75);
8
+ }
9
+
10
+ :where(.bf-theme) :where(.bf-chip, .bf-chip.is-positive, .bf-chip.is-caution, .bf-chip.is-negative, .bf-chip.is-information) {
4
11
  --bf-ui-chip-border: var(--bf-color-border-neutral);
5
12
  --bf-ui-chip-border-hover: var(--bf-color-border-neutral);
6
13
  --bf-ui-chip-border-active: var(--bf-color-border-neutral);
@@ -15,10 +22,14 @@ ${bodyTypeStyles} align-items: baseline;
15
22
  display: inline-flex;
16
23
  gap: 0;
17
24
  inline-size: fit-content;
25
+ justify-content: center;
18
26
  justify-self: start;
19
- margin: 0 var(--bf-ui-chip-padding-inline) var(--bf-single-line-row-margin-block-end) 0;
27
+ margin: 0 var(--bf-component-inline-inset-field) var(--bf-interface-row-compensation-block-end) 0;
20
28
  max-inline-size: 100%;
21
- padding-block: var(--bf-single-line-row-padding-block);
29
+ min-inline-size: min(100%, calc(var(--bf-interface-row-painted-block-size) + (var(--bf-border-width) * 2)));
30
+ padding-block: var(--bf-interface-row-padding-block);
31
+ /* Chips use the Action text keyline. The paint-derived floor only governs
32
+ very short dense labels, keeping them stadium-shaped rather than circular. */
22
33
  padding-inline: max(0rem, calc(var(--bf-ui-chip-padding-inline) - var(--bf-border-width)));
23
34
  position: relative;
24
35
  text-decoration: none;
@@ -111,17 +122,17 @@ ${bodyTypeStyles} color: var(--bf-color-text-muted);
111
122
  ${bodyTypeStyles} align-items: center;
112
123
  background-color: var(--bf-color-text-default);
113
124
  border-radius: 1rem;
114
- box-sizing: content-box;
115
125
  color: var(--bf-color-background-default);
116
126
  display: inline-block;
127
+ inline-size: fit-content;
128
+ justify-self: start;
117
129
  margin: 0;
118
- max-inline-size: 4ch;
119
- min-width: calc(${bodyLineHeight} - (var(--bf-ui-badge-padding-inline) * 2));
120
- overflow: hidden;
130
+ min-inline-size: var(--bf-square-block-size);
121
131
  padding-block: 0;
122
132
  padding-inline: var(--bf-ui-badge-padding-inline);
123
133
  text-align: center;
124
134
  text-indent: 0;
135
+ white-space: nowrap;
125
136
  }
126
137
 
127
138
  :where(.bf-theme) :where(.bf-badge.is-negative) {
@@ -132,7 +143,7 @@ ${bodyTypeStyles} align-items: center;
132
143
  :where(.bf-theme) :where(.bf-chip, .bf-chip.is-positive, .bf-chip.is-caution, .bf-chip.is-negative, .bf-chip.is-information) :where(.bf-badge, .bf-badge.is-negative) {
133
144
  align-self: center;
134
145
  margin-inline-end: var(--bf-ui-badge-overhang);
135
- margin-inline-start: var(--bf-ui-chip-padding-inline);
146
+ margin-inline-start: var(--bf-component-inline-inset-field);
136
147
  }
137
148
 
138
149
  :where(.bf-theme) :where(.bf-status-label, .bf-status-label.is-positive, .bf-status-label.is-caution, .bf-status-label.is-information, .bf-status-label.is-negative) {
@@ -144,11 +155,11 @@ ${bodyTypeStyles} align-items: center;
144
155
  display: inline-block;
145
156
  inline-size: fit-content;
146
157
  justify-self: start;
147
- ${bodyTypeStyles} margin: 0 0 var(--bf-single-line-row-margin-block-end);
158
+ ${bodyTypeStyles} margin: 0 0 var(--bf-interface-row-compensation-block-end);
148
159
  /* Status paint is physically symmetric and shares the same occupied block
149
160
  as a button; typography still comes from the body role. */
150
- padding-block: var(--bf-single-line-row-padding-block);
151
- padding-inline: var(--bf-ui-chip-padding-inline);
161
+ padding-block: var(--bf-interface-row-padding-block);
162
+ padding-inline: var(--bf-component-inline-inset-field);
152
163
  text-align: center;
153
164
  text-decoration: none;
154
165
  white-space: nowrap;
@@ -178,14 +189,25 @@ ${bodyTypeStyles} margin: 0 0 var(--bf-single-line-row-margin-block-end);
178
189
  carrying a second standalone occupied-row contract. This modifier is
179
190
  explicit so component density never changes merely because of ancestry. */
180
191
  :where(.bf-theme) :where(.bf-chip.is-nested, .bf-status-label.is-nested) {
181
- line-height: var(--bf-nested-auxiliary-line-height);
192
+ line-height: var(--bf-nested-row-line-height);
182
193
  margin-block: 0;
183
- padding-block: var(--bf-nested-auxiliary-padding-block);
194
+ padding-block: var(--bf-nested-row-padding-block);
184
195
  }
185
196
 
186
197
  :where(.bf-theme) :where(.bf-chip.is-nested) {
187
198
  border: 0;
188
199
  box-shadow: inset 0 0 0 var(--bf-border-width) var(--bf-ui-chip-border);
200
+ min-inline-size: min(100%, calc(var(--bf-nested-row-painted-block-size) + (var(--bf-border-width) * 2)));
201
+ /* The nested border is inset paint rather than box geometry, so the glyph
202
+ needs the complete Action inset instead of subtracting a real border. */
203
+ padding-inline: var(--bf-ui-chip-padding-inline);
204
+ }
205
+
206
+ /* Chip parts normally carry the standalone body line explicitly. A nested
207
+ chip must pass its reduced line through to those parts so a nested badge
208
+ can fit without making the host row taller. */
209
+ :where(.bf-theme) :where(.bf-chip.is-nested) :where(.bf-chip-lead, .bf-chip-value) {
210
+ line-height: inherit;
189
211
  }
190
212
 
191
213
  :where(.bf-theme) :where(.bf-chip.is-nested:hover) {
@@ -202,7 +224,7 @@ ${bodyTypeStyles} margin: 0 0 var(--bf-single-line-row-margin-block-end);
202
224
 
203
225
  :where(.bf-theme) :where(.bf-badge.is-nested) {
204
226
  align-self: center;
205
- line-height: var(--bf-nested-auxiliary-line-height);
227
+ line-height: var(--bf-nested-row-line-height);
206
228
  vertical-align: middle;
207
229
  }
208
230
  `;
@@ -160,7 +160,7 @@ export function contentCardCss() {
160
160
  grid-template-rows: minmax(0, 1fr);
161
161
  min-inline-size: 0;
162
162
  overflow: hidden;
163
- padding-inline: var(--bf-space-2);
163
+ padding-inline: var(--bf-panel-padding-inline);
164
164
  }
165
165
 
166
166
  :where(.bf-theme) :where(.bf-content-card-primary, .bf-content-card-description-panel) {
@@ -265,7 +265,7 @@ export function contentCardCss() {
265
265
  color: var(--bf-color-text-muted);
266
266
  display: flex;
267
267
  flex-wrap: wrap;
268
- gap: 0 var(--bf-space-half);
268
+ gap: 0 var(--bf-leading-mark-gap);
269
269
  min-inline-size: 0;
270
270
  padding-block-end: var(--bf-space-1);
271
271
  }
@@ -317,7 +317,7 @@ export function contentCardCss() {
317
317
  :where(.bf-theme) :where(.bf-content-card-footer-inner) {
318
318
  align-items: center;
319
319
  display: flex;
320
- gap: var(--bf-space-1);
320
+ gap: var(--bf-leading-mark-gap);
321
321
  inline-size: 100%;
322
322
  mask-image: linear-gradient(to right, #000 85%, transparent 100%);
323
323
  min-block-size: var(--bf-space-4);
@@ -325,7 +325,7 @@ export function contentCardCss() {
325
325
  overflow-x: auto;
326
326
  padding-block-end: var(--bf-space-1);
327
327
  padding-block-start: calc(0.5rem - var(--bf-border-width));
328
- padding-inline: var(--bf-space-2);
328
+ padding-inline: var(--bf-panel-padding-inline);
329
329
  position: relative;
330
330
  scrollbar-width: none;
331
331
  z-index: 2;
@@ -348,7 +348,7 @@ export function contentCardCss() {
348
348
  :where(.bf-theme) :where(.bf-content-card-resource) {
349
349
  align-items: center;
350
350
  display: inline-flex;
351
- gap: var(--bf-space-half);
351
+ gap: var(--bf-leading-mark-gap);
352
352
  }
353
353
 
354
354
  :where(.bf-theme) :where(.bf-content-card-resource > *) {
@@ -357,7 +357,7 @@ export function contentCardCss() {
357
357
 
358
358
  :where(.bf-theme) :where(.bf-content-card-footer .bf-chip) {
359
359
  margin: 0;
360
- margin-inline-end: var(--bf-space-3);
360
+ margin-inline-end: var(--bf-component-inline-inset-continuation);
361
361
  }
362
362
 
363
363
  /* Internal geometry responds to the card's allocated width. The card root is
@@ -376,10 +376,10 @@ export function contentCardCss() {
376
376
 
377
377
  @container bf-content-card (width >= 28.75rem) {
378
378
  :where(.bf-theme) :where(.bf-content-card:is(.is-cols-4, .is-cols-6)) :where(.bf-content-card-frame) {
379
- column-gap: var(--bf-space-2);
379
+ column-gap: var(--bf-panel-padding-inline);
380
380
  flex-direction: row;
381
381
  min-block-size: calc((var(--bf-space-12) * 2) - (var(--bf-border-width) * 2));
382
- padding: var(--bf-space-2) var(--bf-space-2) 0;
382
+ padding: var(--bf-space-2) var(--bf-panel-padding-inline) 0;
383
383
  }
384
384
 
385
385
  :where(.bf-theme) :where(.bf-content-card:is(.is-cols-4, .is-cols-6):not(.is-image)) :where(.bf-content-card-frame) {
@@ -425,7 +425,7 @@ export function contentCardCss() {
425
425
  }
426
426
 
427
427
  :where(.bf-theme) :where(.bf-content-card.is-image-top:is(.is-cols-4, .is-cols-6)) :where(.bf-content-card-body, .bf-content-card-footer-inner) {
428
- padding-inline: var(--bf-space-2);
428
+ padding-inline: var(--bf-panel-padding-inline);
429
429
  }
430
430
  }
431
431
 
@@ -450,7 +450,8 @@ export function contentCardCss() {
450
450
  block-size: 100%;
451
451
  grid-column: 2;
452
452
  grid-row: 1;
453
- padding: var(--bf-space-1) var(--bf-space-1) 0;
453
+ padding-block: var(--bf-space-1) 0;
454
+ padding-inline: var(--bf-component-inline-inset-field);
454
455
  }
455
456
 
456
457
  :where(.bf-theme) :where(.bf-content-card.is-cols-8) :where(.bf-content-card-image) {
@@ -0,0 +1,12 @@
1
+ type ControlGeometryCssOptions = {
2
+ bodyLineHeight: string;
3
+ bodySelectedStartNudge: string;
4
+ };
5
+ export declare function alignedVisualStart(lineHeight: string, visualSize: string, startNudge: string, offset?: string): string;
6
+ /**
7
+ * Shared geometry for single-line controls and prose marks that align with
8
+ * those controls. Component selectors consume these facts; they do not
9
+ * redefine the alignment model locally.
10
+ */
11
+ export declare function controlGeometryCss(options: ControlGeometryCssOptions): string;
12
+ export {};
@@ -0,0 +1,22 @@
1
+ export function alignedVisualStart(lineHeight, visualSize, startNudge, offset = "0rem") {
2
+ const base = `${startNudge} + ((${lineHeight} - ${visualSize}) / 2)`;
3
+ return offset === "0rem" ? `calc(${base})` : `calc(${base} + ${offset})`;
4
+ }
5
+ /**
6
+ * Shared geometry for single-line controls and prose marks that align with
7
+ * those controls. Component selectors consume these facts; they do not
8
+ * redefine the alignment model locally.
9
+ */
10
+ export function controlGeometryCss(options) {
11
+ const { bodyLineHeight, bodySelectedStartNudge } = options;
12
+ return `:where(.bf-theme) {
13
+ --bf-slider-track-size: calc(var(--bf-baseline) * 0.25);
14
+ --bf-slider-row-block-size: var(--bf-interface-row-occupied-block-size);
15
+ --bf-slider-track-offset: ${alignedVisualStart(bodyLineHeight, "var(--bf-slider-track-size)", bodySelectedStartNudge)};
16
+ --bf-switch-track-offset: var(--bf-interface-row-visual-offset);
17
+ --bf-tick-box-offset: var(--bf-interface-row-visual-offset);
18
+ --bf-radio-dot-size: calc((var(--bf-control-visual-size) * 0.375) + var(--bf-border-width));
19
+ --bf-list-marker-dot-size: calc(var(--bf-border-width) * 4);
20
+ }
21
+ `;
22
+ }
@@ -11,7 +11,7 @@ export function ctaFigureAspectCss() {
11
11
 
12
12
  :where(.bf-theme) :where(.bf-cta-block) {
13
13
  align-items: baseline;
14
- column-gap: var(--bf-space-2);
14
+ column-gap: var(--bf-component-inline-inset-action);
15
15
  display: flex;
16
16
  flex-wrap: wrap;
17
17
  margin-block-end: 0;
@@ -48,7 +48,7 @@ export function ctaFigureAspectCss() {
48
48
  :where(.bf-theme) :where(.bf-figure.is-light-inset) > :where(img, picture, video, canvas, svg, .bf-aspect) {
49
49
  background: #fff;
50
50
  box-sizing: border-box;
51
- padding: var(--bf-space-2);
51
+ padding: var(--bf-space-2) var(--bf-panel-padding-inline);
52
52
  }
53
53
 
54
54
  :where(.bf-theme) :where(.bf-figure-caption) {