@echelon-foundry/design-system 0.9.0 → 0.10.0-main.257.1
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/CHANGELOG.md +36 -0
- package/contracts/color-contract.json +1 -1
- package/contracts/diagram-presentation.json +1 -0
- package/dist/all.css +77 -12
- package/dist/build-metrics.json +6 -4
- package/dist/components.css +51 -8
- package/dist/icons/registry.json +1 -1
- package/dist/marketing/forma-all.css +78 -13
- package/dist/marketing/forma-echelon-marketing.css +27 -5
- package/dist/marketing/forma-marketing-theme-echelon.css +1 -1
- package/dist/marketing/forma-marketing.css +27 -5
- package/dist/marketing/forma-marketing.manifest.json +10 -10
- package/dist/marketing/forma-marketing.sha256 +4 -4
- package/dist/marketing.css +26 -4
- package/dist/runtime.js +57 -0
- package/package.json +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,42 @@ published as GitHub release tarballs
|
|
|
7
7
|
and to npm. Earlier releases are described in `docs/CONSUMING-FORMA.md` and
|
|
8
8
|
`HANDOFF.md`.
|
|
9
9
|
|
|
10
|
+
## 0.10.0
|
|
11
|
+
|
|
12
|
+
Minor release: layout constraint enforcement (LCE-001, `experiments/LCE-001/REPORT.md`). `@echelon-foundry/forma-workflow` 1.1.0 ships the matching engine.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Pill diagram nodes.** The curve no longer cuts multi-line text. The corner radius is bounded by the node padding, so a short pill stays a stadium.
|
|
16
|
+
- **Ellipse diagram nodes.** Padding is the inscribed bound (14.65% plus clearance).
|
|
17
|
+
- **Diamond diagram nodes.** Padding is now 25% plus clearance; at 20%, the content corners fell outside the diamond.
|
|
18
|
+
- **Marketing card, badge and button.** A brand `--ef-shape-radius` is capped by each component's padding, so a pill or 50% brand radius cannot cut multi-line content.
|
|
19
|
+
- **Marketing button and badge.** They use `overflow-wrap: anywhere`, so a word wider than the row breaks instead of overflowing.
|
|
20
|
+
- **Diagrams inside `.ef-site`.** They keep their own type metrics: `.ef-diagram` pins its type context, and `revert-layer` stops site prose defaults at the diagram.
|
|
21
|
+
- **Wizard step indicators.** They widen into a pill for multi-digit text instead of overflowing the circle.
|
|
22
|
+
- **Connector labels.** They wrap at 12rem.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- **Node sizing in `Forma.Workflow`.** Sizing is shape-aware and uses a script-aware text estimate (`LayoutText`) that reserves WCAG 1.4.12 text-spacing room. Nodes can be taller than before.
|
|
26
|
+
- **Rank gaps.** They fit the widest connector label.
|
|
27
|
+
- **Connector routing.** Connectors avoid unrelated nodes.
|
|
28
|
+
- **Label placement.** Labels are placed without collisions.
|
|
29
|
+
- **Diagram geometry.** It is emitted in rem (1rem = 16 canvas units), so it scales with enlarged text. The exported example documents were regenerated.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- **`Forma.Workflow.LayoutGeometry`.** Shape geometry: CSS radius normalisation, inner borders, curved and rotated boundaries.
|
|
33
|
+
- **`Forma.Workflow.LayoutCheck`.** Static layout validation: authored sizes too small for their content, node overlap, connectors through nodes, and label collisions.
|
|
34
|
+
- **`tests/layout` and `npm run test:layout`.** An adversarial F# layout probe with browser measurement, plus 50 regression fixtures, including the three Causa historical diagrams.
|
|
35
|
+
|
|
36
|
+
## 0.9.1
|
|
37
|
+
|
|
38
|
+
Patch release: deterministic browser runtime version diagnostics.
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
- The `@echelon-foundry/design-system/runtime` entry point, generated from the package version, registers Forma 0.9.1 in `window.__ECHELON_RUNTIME__` and emits one compact console diagnostic.
|
|
42
|
+
- Cross-browser Playwright coverage for actual build metadata, independent load order, duplicate initialization, conflict diagnostics, and the combined Echelon registry.
|
|
43
|
+
|
|
44
|
+
Component HTML and CSS remain behavior-free. The diagnostic does not inspect or modify the DOM and adds no production dependency.
|
|
45
|
+
|
|
10
46
|
## 0.9.0
|
|
11
47
|
|
|
12
48
|
Minor release: public semantic color contracts and rendered state validation.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "forma.color-contract/1.0.0",
|
|
3
|
-
"formaVersion": "0.
|
|
3
|
+
"formaVersion": "0.10.0",
|
|
4
4
|
"validationScope": "CSS var() references to --ef-color-* plus reserved internal color-related prefixes; other Forma APIs are governed by their own contracts.",
|
|
5
5
|
"compatibility": {
|
|
6
6
|
"policy": "Validate with the contract shipped by the pinned Forma package version.",
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"canvasHeight": "--ef-diagram-canvas-h",
|
|
82
82
|
"portOffset": "--ef-diagram-port-offset",
|
|
83
83
|
"unit": "px",
|
|
84
|
+
"unitNote": "Any CSS length is accepted. Forma.Workflow emits rem (1rem = 16 canvas px), so diagram geometry scales with enlarged text (LCE-001).",
|
|
84
85
|
"axes": "physical (does not mirror in right-to-left documents)"
|
|
85
86
|
},
|
|
86
87
|
"color": {
|
package/dist/all.css
CHANGED
|
@@ -1709,6 +1709,11 @@
|
|
|
1709
1709
|
vertical-align: middle;
|
|
1710
1710
|
}
|
|
1711
1711
|
|
|
1712
|
+
/*
|
|
1713
|
+
* The trigger is a circle for a single glyph (the canonical "?"). LCE-001
|
|
1714
|
+
* measured multi-character text ("1.5k") crossing the curve; that is outside
|
|
1715
|
+
* this contract, not a supported variant.
|
|
1716
|
+
*/
|
|
1712
1717
|
.ef-tooltip__trigger {
|
|
1713
1718
|
min-inline-size: 2.75rem;
|
|
1714
1719
|
min-block-size: 2.75rem;
|
|
@@ -2840,13 +2845,16 @@
|
|
|
2840
2845
|
font-size: 0.8125rem;
|
|
2841
2846
|
}
|
|
2842
2847
|
|
|
2848
|
+
/* A circle for one or two digits; longer step text widens it into a pill (LCE-001). */
|
|
2843
2849
|
.ef-wizard__steps li > span {
|
|
2844
2850
|
display: grid;
|
|
2845
2851
|
place-items: center;
|
|
2846
|
-
inline-size: 1.75rem;
|
|
2852
|
+
min-inline-size: 1.75rem;
|
|
2847
2853
|
block-size: 1.75rem;
|
|
2854
|
+
padding-inline: 0.375rem;
|
|
2848
2855
|
border: 1px solid var(--ef-color-border-functional, #3a403c);
|
|
2849
|
-
border-radius:
|
|
2856
|
+
border-radius: 999px;
|
|
2857
|
+
white-space: nowrap;
|
|
2850
2858
|
}
|
|
2851
2859
|
|
|
2852
2860
|
.ef-wizard__steps [data-state="complete"] > span::after { content: "✓"; position: absolute; }
|
|
@@ -5476,7 +5484,25 @@
|
|
|
5476
5484
|
* Non-color cues: the visible node kind text, data-ef-shape, data-ef-line and
|
|
5477
5485
|
* the textual relationship list. Focus never reads authored color.
|
|
5478
5486
|
*/
|
|
5479
|
-
|
|
5487
|
+
/*
|
|
5488
|
+
* The diagram pins its own type context: node geometry is computed for these
|
|
5489
|
+
* metrics, so a host's font, line height or spacing must not change them
|
|
5490
|
+
* (LCE-001: a diagram inside a marketing card inherited its typography and
|
|
5491
|
+
* outgrew its layout). User text-spacing overrides still apply.
|
|
5492
|
+
*/
|
|
5493
|
+
.ef-diagram {
|
|
5494
|
+
display: grid;
|
|
5495
|
+
grid-template-columns: minmax(0, 1fr);
|
|
5496
|
+
gap: var(--ef-primitive-spacing-4, 1rem);
|
|
5497
|
+
min-inline-size: 0;
|
|
5498
|
+
margin: 0;
|
|
5499
|
+
font-family: var(--ef-primitive-font-family-sans, system-ui, sans-serif);
|
|
5500
|
+
font-size: 1rem;
|
|
5501
|
+
font-weight: 400;
|
|
5502
|
+
line-height: 1.6;
|
|
5503
|
+
letter-spacing: normal;
|
|
5504
|
+
word-spacing: normal;
|
|
5505
|
+
}
|
|
5480
5506
|
.ef-diagram > figcaption { font-weight: 700; }
|
|
5481
5507
|
.ef-diagram__viewport {
|
|
5482
5508
|
max-inline-size: 100%;
|
|
@@ -5516,7 +5542,18 @@
|
|
|
5516
5542
|
overflow-wrap: anywhere;
|
|
5517
5543
|
}
|
|
5518
5544
|
.ef-diagram-node[data-ef-shape="rounded"] { border-radius: var(--ef-primitive-spacing-3, 0.75rem); }
|
|
5519
|
-
|
|
5545
|
+
/*
|
|
5546
|
+
* A pill is a stadium while it is short. A taller (multi-line) pill keeps
|
|
5547
|
+
* the largest corner radius whose curve stays 4px clear of the content box
|
|
5548
|
+
* (LCE-001), instead of letting the curve cut through the text: for inline
|
|
5549
|
+
* padding pi, block padding pb (pb <= pi), border b and clearance c the
|
|
5550
|
+
* bound is pi + pb·(1 + √2) - c·(1 + √2) + b, about 45px with Forma's
|
|
5551
|
+
* spacing tokens. It follows the padding tokens, so it holds at any text size.
|
|
5552
|
+
*/
|
|
5553
|
+
.ef-diagram-node[data-ef-shape="pill"] {
|
|
5554
|
+
border-radius: min(999px, calc(var(--ef-primitive-spacing-5, 1.5rem) + var(--ef-primitive-spacing-3, 0.75rem) * 2.414 - 7.66px));
|
|
5555
|
+
padding-inline: var(--ef-primitive-spacing-5, 1.5rem);
|
|
5556
|
+
}
|
|
5520
5557
|
/*
|
|
5521
5558
|
* FMD-M2 (forma#88). Ellipse and diamond keep a real border (never a
|
|
5522
5559
|
* background-only shape) so the boundary survives backgrounds-off print and
|
|
@@ -5527,7 +5564,8 @@
|
|
|
5527
5564
|
border-radius: 50%;
|
|
5528
5565
|
border-inline-start-width: 2px;
|
|
5529
5566
|
border-block-end: 6px solid var(--ef-diagram-node-accent);
|
|
5530
|
-
|
|
5567
|
+
/* The content's corners stay inside the ellipse: 14.65% is (1 - 1/√2) / 2 of the width, plus clearance (LCE-001). */
|
|
5568
|
+
padding: var(--ef-primitive-spacing-4, 1rem) calc(var(--ef-diagram-w, 12rem) * 0.1465 + 0.25rem);
|
|
5531
5569
|
place-content: center;
|
|
5532
5570
|
text-align: center;
|
|
5533
5571
|
}
|
|
@@ -5535,12 +5573,14 @@
|
|
|
5535
5573
|
.ef-diagram-node[data-ef-shape="diamond"] .ef-diagram-node__meta { grid-template-columns: auto auto; justify-content: center; text-align: start; }
|
|
5536
5574
|
/*
|
|
5537
5575
|
* A square box whose ::before is the diamond; the content sits in the
|
|
5538
|
-
* inscribed square
|
|
5576
|
+
* inscribed square, which is half the side, so the padding is a quarter of
|
|
5577
|
+
* the width plus clearance (LCE-001; 0.2 put content corners outside the
|
|
5578
|
+
* diamond). Padding is derived from --ef-diagram-w because percentage
|
|
5539
5579
|
* padding would resolve against the canvas, not the node.
|
|
5540
5580
|
*/
|
|
5541
5581
|
.ef-diagram-node[data-ef-shape="diamond"] {
|
|
5542
5582
|
aspect-ratio: 1;
|
|
5543
|
-
padding: calc(var(--ef-diagram-w, 12rem) * 0.
|
|
5583
|
+
padding: calc(var(--ef-diagram-w, 12rem) * 0.25 + 0.25rem);
|
|
5544
5584
|
border: 0;
|
|
5545
5585
|
background: transparent;
|
|
5546
5586
|
place-content: center;
|
|
@@ -5580,7 +5620,10 @@
|
|
|
5580
5620
|
background: var(--ef-color-surface-primary, #f2efe7);
|
|
5581
5621
|
color: var(--ef-color-text-primary, #171a18);
|
|
5582
5622
|
font-size: 0.875rem;
|
|
5583
|
-
|
|
5623
|
+
/* One line up to 12rem, then wrapping: the layout reserves that box (LCE-001). */
|
|
5624
|
+
inline-size: max-content;
|
|
5625
|
+
max-inline-size: 12rem;
|
|
5626
|
+
overflow-wrap: anywhere;
|
|
5584
5627
|
}
|
|
5585
5628
|
|
|
5586
5629
|
/* The text equivalent: every relationship in reading order. */
|
|
@@ -7786,6 +7829,14 @@
|
|
|
7786
7829
|
opacity: var(--ef-site-backdrop-opacity, 1);
|
|
7787
7830
|
}
|
|
7788
7831
|
|
|
7832
|
+
/*
|
|
7833
|
+
* Prose defaults stop at an embedded diagram (LCE-001). Diagram geometry is
|
|
7834
|
+
* computed for the component layer's type metrics; a site heading size or
|
|
7835
|
+
* paragraph margin made nodes outgrow their layout. revert-layer hands these
|
|
7836
|
+
* elements back to the earlier layers.
|
|
7837
|
+
*/
|
|
7838
|
+
.ef-site .ef-diagram :where(h1, h2, h3, h4, h5, h6, p, ul, ol, li, a, svg, dl, dt, dd, strong, b) { all: revert-layer; }
|
|
7839
|
+
|
|
7789
7840
|
.ef-site :where(h1, h2, h3, h4, h5, h6) {
|
|
7790
7841
|
margin-block: 0;
|
|
7791
7842
|
color: var(--ef-tone-heading);
|
|
@@ -8084,7 +8135,8 @@
|
|
|
8084
8135
|
min-block-size: calc(var(--ef-layout-target-min) + var(--ef-space-2));
|
|
8085
8136
|
padding: var(--ef-space-3) var(--ef-space-5);
|
|
8086
8137
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule-strong);
|
|
8087
|
-
|
|
8138
|
+
/* Bounded so a brand radius never reaches the content (LCE-001, see .ef-card). */
|
|
8139
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-5) + var(--ef-space-3) * 2.414 - 2.414px));
|
|
8088
8140
|
background: transparent;
|
|
8089
8141
|
color: var(--ef-tone-text);
|
|
8090
8142
|
font: inherit;
|
|
@@ -8096,7 +8148,10 @@
|
|
|
8096
8148
|
/* Never wider than its row and never split mid-word unless one word is
|
|
8097
8149
|
wider than the row (ULI-L05, L06). */
|
|
8098
8150
|
max-inline-size: 100%;
|
|
8099
|
-
|
|
8151
|
+
/* anywhere, not break-word: inside inline-flex only anywhere lets the
|
|
8152
|
+
min-content width shrink, so a word wider than the row breaks instead of
|
|
8153
|
+
overflowing the button (LCE-001). Words still break only when they must. */
|
|
8154
|
+
overflow-wrap: anywhere;
|
|
8100
8155
|
cursor: pointer;
|
|
8101
8156
|
/* Hover and press are perceptual emphasis on the surface only: the hit
|
|
8102
8157
|
target never moves under the pointer (MOT-014). */
|
|
@@ -8141,11 +8196,13 @@
|
|
|
8141
8196
|
display: inline-flex;
|
|
8142
8197
|
align-items: center;
|
|
8143
8198
|
gap: var(--ef-space-1);
|
|
8199
|
+
max-inline-size: 100%;
|
|
8144
8200
|
padding: var(--ef-space-1) var(--ef-space-2);
|
|
8145
8201
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
8146
|
-
border-radius: var(--ef-shape-radius);
|
|
8202
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-2) + var(--ef-space-1) * 2.414 - 2.414px));
|
|
8147
8203
|
color: var(--ef-tone-text);
|
|
8148
8204
|
font-family: var(--ef-type-family-mono);
|
|
8205
|
+
overflow-wrap: anywhere;
|
|
8149
8206
|
font-size: var(--ef-type-size-label);
|
|
8150
8207
|
line-height: var(--ef-type-leading-snug);
|
|
8151
8208
|
text-decoration: none;
|
|
@@ -8191,7 +8248,15 @@
|
|
|
8191
8248
|
min-inline-size: 0;
|
|
8192
8249
|
padding: var(--ef-layout-block-space);
|
|
8193
8250
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
8194
|
-
|
|
8251
|
+
/*
|
|
8252
|
+
* A brand radius is capped by the padding (LCE-001). Content whose corner
|
|
8253
|
+
* sits at padding p from both edges stays clear of a circular corner of
|
|
8254
|
+
* radius r while r <= (2 + √2)·(p - c) + c for clearance c; with c = 1px
|
|
8255
|
+
* that is p·3.414 - 2.414px. A badge or button uses its own two paddings:
|
|
8256
|
+
* pi + pb·2.414 - 2.414px (pb the smaller). A larger brand radius (a pill
|
|
8257
|
+
* or 50%) would otherwise cut through multi-line content.
|
|
8258
|
+
*/
|
|
8259
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-layout-block-space) * 3.414 - 2.414px));
|
|
8195
8260
|
}
|
|
8196
8261
|
|
|
8197
8262
|
/* Inside a grid, cards share hairline rules instead of doubling borders. */
|
package/dist/build-metrics.json
CHANGED
|
@@ -337,16 +337,17 @@
|
|
|
337
337
|
"patterns/wizard.html",
|
|
338
338
|
"patterns/work-queue.html",
|
|
339
339
|
"patterns/workspace-shell.html",
|
|
340
|
+
"runtime.js",
|
|
340
341
|
"skins.css",
|
|
341
342
|
"tokens.css"
|
|
342
343
|
],
|
|
343
344
|
"bytes": {
|
|
344
|
-
"all.css":
|
|
345
|
+
"all.css": 336382,
|
|
345
346
|
"assessment.css": 36343,
|
|
346
347
|
"brands/echelon.css": 4213,
|
|
347
348
|
"brands/example-harbor.css": 4229,
|
|
348
349
|
"brands/index.json": 335,
|
|
349
|
-
"components.css":
|
|
350
|
+
"components.css": 213356,
|
|
350
351
|
"foundations.css": 15326,
|
|
351
352
|
"icons/add.svg": 216,
|
|
352
353
|
"icons/agent.svg": 354,
|
|
@@ -486,7 +487,7 @@
|
|
|
486
487
|
"icons/phone.svg": 274,
|
|
487
488
|
"icons/print.svg": 318,
|
|
488
489
|
"icons/refresh.svg": 277,
|
|
489
|
-
"icons/registry.json":
|
|
490
|
+
"icons/registry.json": 28724,
|
|
490
491
|
"icons/reply-all.svg": 268,
|
|
491
492
|
"icons/reply.svg": 234,
|
|
492
493
|
"icons/save.svg": 275,
|
|
@@ -509,7 +510,7 @@
|
|
|
509
510
|
"icons/warning.svg": 324,
|
|
510
511
|
"icons/wifi-off.svg": 269,
|
|
511
512
|
"icons/workflow.svg": 345,
|
|
512
|
-
"marketing.css":
|
|
513
|
+
"marketing.css": 47618,
|
|
513
514
|
"patterns/active-filter-summary.html": 532,
|
|
514
515
|
"patterns/alert.html": 384,
|
|
515
516
|
"patterns/allocation.html": 1033,
|
|
@@ -678,6 +679,7 @@
|
|
|
678
679
|
"patterns/wizard.html": 817,
|
|
679
680
|
"patterns/work-queue.html": 971,
|
|
680
681
|
"patterns/workspace-shell.html": 479,
|
|
682
|
+
"runtime.js": 3030,
|
|
681
683
|
"skins.css": 924,
|
|
682
684
|
"tokens.css": 23735
|
|
683
685
|
}
|
package/dist/components.css
CHANGED
|
@@ -799,6 +799,11 @@
|
|
|
799
799
|
vertical-align: middle;
|
|
800
800
|
}
|
|
801
801
|
|
|
802
|
+
/*
|
|
803
|
+
* The trigger is a circle for a single glyph (the canonical "?"). LCE-001
|
|
804
|
+
* measured multi-character text ("1.5k") crossing the curve; that is outside
|
|
805
|
+
* this contract, not a supported variant.
|
|
806
|
+
*/
|
|
802
807
|
.ef-tooltip__trigger {
|
|
803
808
|
min-inline-size: 2.75rem;
|
|
804
809
|
min-block-size: 2.75rem;
|
|
@@ -1930,13 +1935,16 @@
|
|
|
1930
1935
|
font-size: 0.8125rem;
|
|
1931
1936
|
}
|
|
1932
1937
|
|
|
1938
|
+
/* A circle for one or two digits; longer step text widens it into a pill (LCE-001). */
|
|
1933
1939
|
.ef-wizard__steps li > span {
|
|
1934
1940
|
display: grid;
|
|
1935
1941
|
place-items: center;
|
|
1936
|
-
inline-size: 1.75rem;
|
|
1942
|
+
min-inline-size: 1.75rem;
|
|
1937
1943
|
block-size: 1.75rem;
|
|
1944
|
+
padding-inline: 0.375rem;
|
|
1938
1945
|
border: 1px solid var(--ef-color-border-functional, #3a403c);
|
|
1939
|
-
border-radius:
|
|
1946
|
+
border-radius: 999px;
|
|
1947
|
+
white-space: nowrap;
|
|
1940
1948
|
}
|
|
1941
1949
|
|
|
1942
1950
|
.ef-wizard__steps [data-state="complete"] > span::after { content: "✓"; position: absolute; }
|
|
@@ -4566,7 +4574,25 @@
|
|
|
4566
4574
|
* Non-color cues: the visible node kind text, data-ef-shape, data-ef-line and
|
|
4567
4575
|
* the textual relationship list. Focus never reads authored color.
|
|
4568
4576
|
*/
|
|
4569
|
-
|
|
4577
|
+
/*
|
|
4578
|
+
* The diagram pins its own type context: node geometry is computed for these
|
|
4579
|
+
* metrics, so a host's font, line height or spacing must not change them
|
|
4580
|
+
* (LCE-001: a diagram inside a marketing card inherited its typography and
|
|
4581
|
+
* outgrew its layout). User text-spacing overrides still apply.
|
|
4582
|
+
*/
|
|
4583
|
+
.ef-diagram {
|
|
4584
|
+
display: grid;
|
|
4585
|
+
grid-template-columns: minmax(0, 1fr);
|
|
4586
|
+
gap: var(--ef-primitive-spacing-4, 1rem);
|
|
4587
|
+
min-inline-size: 0;
|
|
4588
|
+
margin: 0;
|
|
4589
|
+
font-family: var(--ef-primitive-font-family-sans, system-ui, sans-serif);
|
|
4590
|
+
font-size: 1rem;
|
|
4591
|
+
font-weight: 400;
|
|
4592
|
+
line-height: 1.6;
|
|
4593
|
+
letter-spacing: normal;
|
|
4594
|
+
word-spacing: normal;
|
|
4595
|
+
}
|
|
4570
4596
|
.ef-diagram > figcaption { font-weight: 700; }
|
|
4571
4597
|
.ef-diagram__viewport {
|
|
4572
4598
|
max-inline-size: 100%;
|
|
@@ -4606,7 +4632,18 @@
|
|
|
4606
4632
|
overflow-wrap: anywhere;
|
|
4607
4633
|
}
|
|
4608
4634
|
.ef-diagram-node[data-ef-shape="rounded"] { border-radius: var(--ef-primitive-spacing-3, 0.75rem); }
|
|
4609
|
-
|
|
4635
|
+
/*
|
|
4636
|
+
* A pill is a stadium while it is short. A taller (multi-line) pill keeps
|
|
4637
|
+
* the largest corner radius whose curve stays 4px clear of the content box
|
|
4638
|
+
* (LCE-001), instead of letting the curve cut through the text: for inline
|
|
4639
|
+
* padding pi, block padding pb (pb <= pi), border b and clearance c the
|
|
4640
|
+
* bound is pi + pb·(1 + √2) - c·(1 + √2) + b, about 45px with Forma's
|
|
4641
|
+
* spacing tokens. It follows the padding tokens, so it holds at any text size.
|
|
4642
|
+
*/
|
|
4643
|
+
.ef-diagram-node[data-ef-shape="pill"] {
|
|
4644
|
+
border-radius: min(999px, calc(var(--ef-primitive-spacing-5, 1.5rem) + var(--ef-primitive-spacing-3, 0.75rem) * 2.414 - 7.66px));
|
|
4645
|
+
padding-inline: var(--ef-primitive-spacing-5, 1.5rem);
|
|
4646
|
+
}
|
|
4610
4647
|
/*
|
|
4611
4648
|
* FMD-M2 (forma#88). Ellipse and diamond keep a real border (never a
|
|
4612
4649
|
* background-only shape) so the boundary survives backgrounds-off print and
|
|
@@ -4617,7 +4654,8 @@
|
|
|
4617
4654
|
border-radius: 50%;
|
|
4618
4655
|
border-inline-start-width: 2px;
|
|
4619
4656
|
border-block-end: 6px solid var(--ef-diagram-node-accent);
|
|
4620
|
-
|
|
4657
|
+
/* The content's corners stay inside the ellipse: 14.65% is (1 - 1/√2) / 2 of the width, plus clearance (LCE-001). */
|
|
4658
|
+
padding: var(--ef-primitive-spacing-4, 1rem) calc(var(--ef-diagram-w, 12rem) * 0.1465 + 0.25rem);
|
|
4621
4659
|
place-content: center;
|
|
4622
4660
|
text-align: center;
|
|
4623
4661
|
}
|
|
@@ -4625,12 +4663,14 @@
|
|
|
4625
4663
|
.ef-diagram-node[data-ef-shape="diamond"] .ef-diagram-node__meta { grid-template-columns: auto auto; justify-content: center; text-align: start; }
|
|
4626
4664
|
/*
|
|
4627
4665
|
* A square box whose ::before is the diamond; the content sits in the
|
|
4628
|
-
* inscribed square
|
|
4666
|
+
* inscribed square, which is half the side, so the padding is a quarter of
|
|
4667
|
+
* the width plus clearance (LCE-001; 0.2 put content corners outside the
|
|
4668
|
+
* diamond). Padding is derived from --ef-diagram-w because percentage
|
|
4629
4669
|
* padding would resolve against the canvas, not the node.
|
|
4630
4670
|
*/
|
|
4631
4671
|
.ef-diagram-node[data-ef-shape="diamond"] {
|
|
4632
4672
|
aspect-ratio: 1;
|
|
4633
|
-
padding: calc(var(--ef-diagram-w, 12rem) * 0.
|
|
4673
|
+
padding: calc(var(--ef-diagram-w, 12rem) * 0.25 + 0.25rem);
|
|
4634
4674
|
border: 0;
|
|
4635
4675
|
background: transparent;
|
|
4636
4676
|
place-content: center;
|
|
@@ -4670,7 +4710,10 @@
|
|
|
4670
4710
|
background: var(--ef-color-surface-primary, #f2efe7);
|
|
4671
4711
|
color: var(--ef-color-text-primary, #171a18);
|
|
4672
4712
|
font-size: 0.875rem;
|
|
4673
|
-
|
|
4713
|
+
/* One line up to 12rem, then wrapping: the layout reserves that box (LCE-001). */
|
|
4714
|
+
inline-size: max-content;
|
|
4715
|
+
max-inline-size: 12rem;
|
|
4716
|
+
overflow-wrap: anywhere;
|
|
4674
4717
|
}
|
|
4675
4718
|
|
|
4676
4719
|
/* The text equivalent: every relationship in reading order. */
|
package/dist/icons/registry.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Forma 0.
|
|
1
|
+
/*! Forma 0.10.0 | forma-all.css | bundle echelon-marketing
|
|
2
2
|
Complete Forma surface (application components, assessment, and marketing layers) for sites that also present Forma application patterns such as data grids or status lozenges.
|
|
3
3
|
Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
|
|
4
4
|
|
|
@@ -1714,6 +1714,11 @@
|
|
|
1714
1714
|
vertical-align: middle;
|
|
1715
1715
|
}
|
|
1716
1716
|
|
|
1717
|
+
/*
|
|
1718
|
+
* The trigger is a circle for a single glyph (the canonical "?"). LCE-001
|
|
1719
|
+
* measured multi-character text ("1.5k") crossing the curve; that is outside
|
|
1720
|
+
* this contract, not a supported variant.
|
|
1721
|
+
*/
|
|
1717
1722
|
.ef-tooltip__trigger {
|
|
1718
1723
|
min-inline-size: 2.75rem;
|
|
1719
1724
|
min-block-size: 2.75rem;
|
|
@@ -2845,13 +2850,16 @@
|
|
|
2845
2850
|
font-size: 0.8125rem;
|
|
2846
2851
|
}
|
|
2847
2852
|
|
|
2853
|
+
/* A circle for one or two digits; longer step text widens it into a pill (LCE-001). */
|
|
2848
2854
|
.ef-wizard__steps li > span {
|
|
2849
2855
|
display: grid;
|
|
2850
2856
|
place-items: center;
|
|
2851
|
-
inline-size: 1.75rem;
|
|
2857
|
+
min-inline-size: 1.75rem;
|
|
2852
2858
|
block-size: 1.75rem;
|
|
2859
|
+
padding-inline: 0.375rem;
|
|
2853
2860
|
border: 1px solid var(--ef-color-border-functional, #3a403c);
|
|
2854
|
-
border-radius:
|
|
2861
|
+
border-radius: 999px;
|
|
2862
|
+
white-space: nowrap;
|
|
2855
2863
|
}
|
|
2856
2864
|
|
|
2857
2865
|
.ef-wizard__steps [data-state="complete"] > span::after { content: "✓"; position: absolute; }
|
|
@@ -5481,7 +5489,25 @@
|
|
|
5481
5489
|
* Non-color cues: the visible node kind text, data-ef-shape, data-ef-line and
|
|
5482
5490
|
* the textual relationship list. Focus never reads authored color.
|
|
5483
5491
|
*/
|
|
5484
|
-
|
|
5492
|
+
/*
|
|
5493
|
+
* The diagram pins its own type context: node geometry is computed for these
|
|
5494
|
+
* metrics, so a host's font, line height or spacing must not change them
|
|
5495
|
+
* (LCE-001: a diagram inside a marketing card inherited its typography and
|
|
5496
|
+
* outgrew its layout). User text-spacing overrides still apply.
|
|
5497
|
+
*/
|
|
5498
|
+
.ef-diagram {
|
|
5499
|
+
display: grid;
|
|
5500
|
+
grid-template-columns: minmax(0, 1fr);
|
|
5501
|
+
gap: var(--ef-primitive-spacing-4, 1rem);
|
|
5502
|
+
min-inline-size: 0;
|
|
5503
|
+
margin: 0;
|
|
5504
|
+
font-family: var(--ef-primitive-font-family-sans, system-ui, sans-serif);
|
|
5505
|
+
font-size: 1rem;
|
|
5506
|
+
font-weight: 400;
|
|
5507
|
+
line-height: 1.6;
|
|
5508
|
+
letter-spacing: normal;
|
|
5509
|
+
word-spacing: normal;
|
|
5510
|
+
}
|
|
5485
5511
|
.ef-diagram > figcaption { font-weight: 700; }
|
|
5486
5512
|
.ef-diagram__viewport {
|
|
5487
5513
|
max-inline-size: 100%;
|
|
@@ -5521,7 +5547,18 @@
|
|
|
5521
5547
|
overflow-wrap: anywhere;
|
|
5522
5548
|
}
|
|
5523
5549
|
.ef-diagram-node[data-ef-shape="rounded"] { border-radius: var(--ef-primitive-spacing-3, 0.75rem); }
|
|
5524
|
-
|
|
5550
|
+
/*
|
|
5551
|
+
* A pill is a stadium while it is short. A taller (multi-line) pill keeps
|
|
5552
|
+
* the largest corner radius whose curve stays 4px clear of the content box
|
|
5553
|
+
* (LCE-001), instead of letting the curve cut through the text: for inline
|
|
5554
|
+
* padding pi, block padding pb (pb <= pi), border b and clearance c the
|
|
5555
|
+
* bound is pi + pb·(1 + √2) - c·(1 + √2) + b, about 45px with Forma's
|
|
5556
|
+
* spacing tokens. It follows the padding tokens, so it holds at any text size.
|
|
5557
|
+
*/
|
|
5558
|
+
.ef-diagram-node[data-ef-shape="pill"] {
|
|
5559
|
+
border-radius: min(999px, calc(var(--ef-primitive-spacing-5, 1.5rem) + var(--ef-primitive-spacing-3, 0.75rem) * 2.414 - 7.66px));
|
|
5560
|
+
padding-inline: var(--ef-primitive-spacing-5, 1.5rem);
|
|
5561
|
+
}
|
|
5525
5562
|
/*
|
|
5526
5563
|
* FMD-M2 (forma#88). Ellipse and diamond keep a real border (never a
|
|
5527
5564
|
* background-only shape) so the boundary survives backgrounds-off print and
|
|
@@ -5532,7 +5569,8 @@
|
|
|
5532
5569
|
border-radius: 50%;
|
|
5533
5570
|
border-inline-start-width: 2px;
|
|
5534
5571
|
border-block-end: 6px solid var(--ef-diagram-node-accent);
|
|
5535
|
-
|
|
5572
|
+
/* The content's corners stay inside the ellipse: 14.65% is (1 - 1/√2) / 2 of the width, plus clearance (LCE-001). */
|
|
5573
|
+
padding: var(--ef-primitive-spacing-4, 1rem) calc(var(--ef-diagram-w, 12rem) * 0.1465 + 0.25rem);
|
|
5536
5574
|
place-content: center;
|
|
5537
5575
|
text-align: center;
|
|
5538
5576
|
}
|
|
@@ -5540,12 +5578,14 @@
|
|
|
5540
5578
|
.ef-diagram-node[data-ef-shape="diamond"] .ef-diagram-node__meta { grid-template-columns: auto auto; justify-content: center; text-align: start; }
|
|
5541
5579
|
/*
|
|
5542
5580
|
* A square box whose ::before is the diamond; the content sits in the
|
|
5543
|
-
* inscribed square
|
|
5581
|
+
* inscribed square, which is half the side, so the padding is a quarter of
|
|
5582
|
+
* the width plus clearance (LCE-001; 0.2 put content corners outside the
|
|
5583
|
+
* diamond). Padding is derived from --ef-diagram-w because percentage
|
|
5544
5584
|
* padding would resolve against the canvas, not the node.
|
|
5545
5585
|
*/
|
|
5546
5586
|
.ef-diagram-node[data-ef-shape="diamond"] {
|
|
5547
5587
|
aspect-ratio: 1;
|
|
5548
|
-
padding: calc(var(--ef-diagram-w, 12rem) * 0.
|
|
5588
|
+
padding: calc(var(--ef-diagram-w, 12rem) * 0.25 + 0.25rem);
|
|
5549
5589
|
border: 0;
|
|
5550
5590
|
background: transparent;
|
|
5551
5591
|
place-content: center;
|
|
@@ -5585,7 +5625,10 @@
|
|
|
5585
5625
|
background: var(--ef-color-surface-primary, #f2efe7);
|
|
5586
5626
|
color: var(--ef-color-text-primary, #171a18);
|
|
5587
5627
|
font-size: 0.875rem;
|
|
5588
|
-
|
|
5628
|
+
/* One line up to 12rem, then wrapping: the layout reserves that box (LCE-001). */
|
|
5629
|
+
inline-size: max-content;
|
|
5630
|
+
max-inline-size: 12rem;
|
|
5631
|
+
overflow-wrap: anywhere;
|
|
5589
5632
|
}
|
|
5590
5633
|
|
|
5591
5634
|
/* The text equivalent: every relationship in reading order. */
|
|
@@ -7791,6 +7834,14 @@
|
|
|
7791
7834
|
opacity: var(--ef-site-backdrop-opacity, 1);
|
|
7792
7835
|
}
|
|
7793
7836
|
|
|
7837
|
+
/*
|
|
7838
|
+
* Prose defaults stop at an embedded diagram (LCE-001). Diagram geometry is
|
|
7839
|
+
* computed for the component layer's type metrics; a site heading size or
|
|
7840
|
+
* paragraph margin made nodes outgrow their layout. revert-layer hands these
|
|
7841
|
+
* elements back to the earlier layers.
|
|
7842
|
+
*/
|
|
7843
|
+
.ef-site .ef-diagram :where(h1, h2, h3, h4, h5, h6, p, ul, ol, li, a, svg, dl, dt, dd, strong, b) { all: revert-layer; }
|
|
7844
|
+
|
|
7794
7845
|
.ef-site :where(h1, h2, h3, h4, h5, h6) {
|
|
7795
7846
|
margin-block: 0;
|
|
7796
7847
|
color: var(--ef-tone-heading);
|
|
@@ -8089,7 +8140,8 @@
|
|
|
8089
8140
|
min-block-size: calc(var(--ef-layout-target-min) + var(--ef-space-2));
|
|
8090
8141
|
padding: var(--ef-space-3) var(--ef-space-5);
|
|
8091
8142
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule-strong);
|
|
8092
|
-
|
|
8143
|
+
/* Bounded so a brand radius never reaches the content (LCE-001, see .ef-card). */
|
|
8144
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-5) + var(--ef-space-3) * 2.414 - 2.414px));
|
|
8093
8145
|
background: transparent;
|
|
8094
8146
|
color: var(--ef-tone-text);
|
|
8095
8147
|
font: inherit;
|
|
@@ -8101,7 +8153,10 @@
|
|
|
8101
8153
|
/* Never wider than its row and never split mid-word unless one word is
|
|
8102
8154
|
wider than the row (ULI-L05, L06). */
|
|
8103
8155
|
max-inline-size: 100%;
|
|
8104
|
-
|
|
8156
|
+
/* anywhere, not break-word: inside inline-flex only anywhere lets the
|
|
8157
|
+
min-content width shrink, so a word wider than the row breaks instead of
|
|
8158
|
+
overflowing the button (LCE-001). Words still break only when they must. */
|
|
8159
|
+
overflow-wrap: anywhere;
|
|
8105
8160
|
cursor: pointer;
|
|
8106
8161
|
/* Hover and press are perceptual emphasis on the surface only: the hit
|
|
8107
8162
|
target never moves under the pointer (MOT-014). */
|
|
@@ -8146,11 +8201,13 @@
|
|
|
8146
8201
|
display: inline-flex;
|
|
8147
8202
|
align-items: center;
|
|
8148
8203
|
gap: var(--ef-space-1);
|
|
8204
|
+
max-inline-size: 100%;
|
|
8149
8205
|
padding: var(--ef-space-1) var(--ef-space-2);
|
|
8150
8206
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
8151
|
-
border-radius: var(--ef-shape-radius);
|
|
8207
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-2) + var(--ef-space-1) * 2.414 - 2.414px));
|
|
8152
8208
|
color: var(--ef-tone-text);
|
|
8153
8209
|
font-family: var(--ef-type-family-mono);
|
|
8210
|
+
overflow-wrap: anywhere;
|
|
8154
8211
|
font-size: var(--ef-type-size-label);
|
|
8155
8212
|
line-height: var(--ef-type-leading-snug);
|
|
8156
8213
|
text-decoration: none;
|
|
@@ -8196,7 +8253,15 @@
|
|
|
8196
8253
|
min-inline-size: 0;
|
|
8197
8254
|
padding: var(--ef-layout-block-space);
|
|
8198
8255
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
8199
|
-
|
|
8256
|
+
/*
|
|
8257
|
+
* A brand radius is capped by the padding (LCE-001). Content whose corner
|
|
8258
|
+
* sits at padding p from both edges stays clear of a circular corner of
|
|
8259
|
+
* radius r while r <= (2 + √2)·(p - c) + c for clearance c; with c = 1px
|
|
8260
|
+
* that is p·3.414 - 2.414px. A badge or button uses its own two paddings:
|
|
8261
|
+
* pi + pb·2.414 - 2.414px (pb the smaller). A larger brand radius (a pill
|
|
8262
|
+
* or 50%) would otherwise cut through multi-line content.
|
|
8263
|
+
*/
|
|
8264
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-layout-block-space) * 3.414 - 2.414px));
|
|
8200
8265
|
}
|
|
8201
8266
|
|
|
8202
8267
|
/* Inside a grid, cards share hairline rules instead of doubling borders. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Forma 0.
|
|
1
|
+
/*! Forma 0.10.0 | forma-echelon-marketing.css | bundle echelon-marketing
|
|
2
2
|
Single-file Echelon marketing presentation: forma-marketing.css followed by the Echelon theme. Most sites load only this file.
|
|
3
3
|
Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
|
|
4
4
|
|
|
@@ -1137,6 +1137,14 @@
|
|
|
1137
1137
|
opacity: var(--ef-site-backdrop-opacity, 1);
|
|
1138
1138
|
}
|
|
1139
1139
|
|
|
1140
|
+
/*
|
|
1141
|
+
* Prose defaults stop at an embedded diagram (LCE-001). Diagram geometry is
|
|
1142
|
+
* computed for the component layer's type metrics; a site heading size or
|
|
1143
|
+
* paragraph margin made nodes outgrow their layout. revert-layer hands these
|
|
1144
|
+
* elements back to the earlier layers.
|
|
1145
|
+
*/
|
|
1146
|
+
.ef-site .ef-diagram :where(h1, h2, h3, h4, h5, h6, p, ul, ol, li, a, svg, dl, dt, dd, strong, b) { all: revert-layer; }
|
|
1147
|
+
|
|
1140
1148
|
.ef-site :where(h1, h2, h3, h4, h5, h6) {
|
|
1141
1149
|
margin-block: 0;
|
|
1142
1150
|
color: var(--ef-tone-heading);
|
|
@@ -1436,7 +1444,8 @@
|
|
|
1436
1444
|
min-block-size: calc(var(--ef-layout-target-min) + var(--ef-space-2));
|
|
1437
1445
|
padding: var(--ef-space-3) var(--ef-space-5);
|
|
1438
1446
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule-strong);
|
|
1439
|
-
|
|
1447
|
+
/* Bounded so a brand radius never reaches the content (LCE-001, see .ef-card). */
|
|
1448
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-5) + var(--ef-space-3) * 2.414 - 2.414px));
|
|
1440
1449
|
background: transparent;
|
|
1441
1450
|
color: var(--ef-tone-text);
|
|
1442
1451
|
font: inherit;
|
|
@@ -1448,7 +1457,10 @@
|
|
|
1448
1457
|
/* Never wider than its row and never split mid-word unless one word is
|
|
1449
1458
|
wider than the row (ULI-L05, L06). */
|
|
1450
1459
|
max-inline-size: 100%;
|
|
1451
|
-
|
|
1460
|
+
/* anywhere, not break-word: inside inline-flex only anywhere lets the
|
|
1461
|
+
min-content width shrink, so a word wider than the row breaks instead of
|
|
1462
|
+
overflowing the button (LCE-001). Words still break only when they must. */
|
|
1463
|
+
overflow-wrap: anywhere;
|
|
1452
1464
|
cursor: pointer;
|
|
1453
1465
|
/* Hover and press are perceptual emphasis on the surface only: the hit
|
|
1454
1466
|
target never moves under the pointer (MOT-014). */
|
|
@@ -1493,11 +1505,13 @@
|
|
|
1493
1505
|
display: inline-flex;
|
|
1494
1506
|
align-items: center;
|
|
1495
1507
|
gap: var(--ef-space-1);
|
|
1508
|
+
max-inline-size: 100%;
|
|
1496
1509
|
padding: var(--ef-space-1) var(--ef-space-2);
|
|
1497
1510
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
1498
|
-
border-radius: var(--ef-shape-radius);
|
|
1511
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-2) + var(--ef-space-1) * 2.414 - 2.414px));
|
|
1499
1512
|
color: var(--ef-tone-text);
|
|
1500
1513
|
font-family: var(--ef-type-family-mono);
|
|
1514
|
+
overflow-wrap: anywhere;
|
|
1501
1515
|
font-size: var(--ef-type-size-label);
|
|
1502
1516
|
line-height: var(--ef-type-leading-snug);
|
|
1503
1517
|
text-decoration: none;
|
|
@@ -1543,7 +1557,15 @@
|
|
|
1543
1557
|
min-inline-size: 0;
|
|
1544
1558
|
padding: var(--ef-layout-block-space);
|
|
1545
1559
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
1546
|
-
|
|
1560
|
+
/*
|
|
1561
|
+
* A brand radius is capped by the padding (LCE-001). Content whose corner
|
|
1562
|
+
* sits at padding p from both edges stays clear of a circular corner of
|
|
1563
|
+
* radius r while r <= (2 + √2)·(p - c) + c for clearance c; with c = 1px
|
|
1564
|
+
* that is p·3.414 - 2.414px. A badge or button uses its own two paddings:
|
|
1565
|
+
* pi + pb·2.414 - 2.414px (pb the smaller). A larger brand radius (a pill
|
|
1566
|
+
* or 50%) would otherwise cut through multi-line content.
|
|
1567
|
+
*/
|
|
1568
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-layout-block-space) * 3.414 - 2.414px));
|
|
1547
1569
|
}
|
|
1548
1570
|
|
|
1549
1571
|
/* Inside a grid, cards share hairline rules instead of doubling borders. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Forma 0.
|
|
1
|
+
/*! Forma 0.10.0 | forma-marketing-theme-echelon.css | bundle echelon-marketing
|
|
2
2
|
Echelon Marketing Theme: compiled theme tokens and theme hooks. Load after forma-marketing.css.
|
|
3
3
|
Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Forma 0.
|
|
1
|
+
/*! Forma 0.10.0 | forma-marketing.css | bundle echelon-marketing
|
|
2
2
|
Brand-neutral marketing layer: core tokens, role defaults, Forma foundations, marketing foundations, components, and layouts.
|
|
3
3
|
Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
|
|
4
4
|
|
|
@@ -1136,6 +1136,14 @@
|
|
|
1136
1136
|
opacity: var(--ef-site-backdrop-opacity, 1);
|
|
1137
1137
|
}
|
|
1138
1138
|
|
|
1139
|
+
/*
|
|
1140
|
+
* Prose defaults stop at an embedded diagram (LCE-001). Diagram geometry is
|
|
1141
|
+
* computed for the component layer's type metrics; a site heading size or
|
|
1142
|
+
* paragraph margin made nodes outgrow their layout. revert-layer hands these
|
|
1143
|
+
* elements back to the earlier layers.
|
|
1144
|
+
*/
|
|
1145
|
+
.ef-site .ef-diagram :where(h1, h2, h3, h4, h5, h6, p, ul, ol, li, a, svg, dl, dt, dd, strong, b) { all: revert-layer; }
|
|
1146
|
+
|
|
1139
1147
|
.ef-site :where(h1, h2, h3, h4, h5, h6) {
|
|
1140
1148
|
margin-block: 0;
|
|
1141
1149
|
color: var(--ef-tone-heading);
|
|
@@ -1435,7 +1443,8 @@
|
|
|
1435
1443
|
min-block-size: calc(var(--ef-layout-target-min) + var(--ef-space-2));
|
|
1436
1444
|
padding: var(--ef-space-3) var(--ef-space-5);
|
|
1437
1445
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule-strong);
|
|
1438
|
-
|
|
1446
|
+
/* Bounded so a brand radius never reaches the content (LCE-001, see .ef-card). */
|
|
1447
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-5) + var(--ef-space-3) * 2.414 - 2.414px));
|
|
1439
1448
|
background: transparent;
|
|
1440
1449
|
color: var(--ef-tone-text);
|
|
1441
1450
|
font: inherit;
|
|
@@ -1447,7 +1456,10 @@
|
|
|
1447
1456
|
/* Never wider than its row and never split mid-word unless one word is
|
|
1448
1457
|
wider than the row (ULI-L05, L06). */
|
|
1449
1458
|
max-inline-size: 100%;
|
|
1450
|
-
|
|
1459
|
+
/* anywhere, not break-word: inside inline-flex only anywhere lets the
|
|
1460
|
+
min-content width shrink, so a word wider than the row breaks instead of
|
|
1461
|
+
overflowing the button (LCE-001). Words still break only when they must. */
|
|
1462
|
+
overflow-wrap: anywhere;
|
|
1451
1463
|
cursor: pointer;
|
|
1452
1464
|
/* Hover and press are perceptual emphasis on the surface only: the hit
|
|
1453
1465
|
target never moves under the pointer (MOT-014). */
|
|
@@ -1492,11 +1504,13 @@
|
|
|
1492
1504
|
display: inline-flex;
|
|
1493
1505
|
align-items: center;
|
|
1494
1506
|
gap: var(--ef-space-1);
|
|
1507
|
+
max-inline-size: 100%;
|
|
1495
1508
|
padding: var(--ef-space-1) var(--ef-space-2);
|
|
1496
1509
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
1497
|
-
border-radius: var(--ef-shape-radius);
|
|
1510
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-2) + var(--ef-space-1) * 2.414 - 2.414px));
|
|
1498
1511
|
color: var(--ef-tone-text);
|
|
1499
1512
|
font-family: var(--ef-type-family-mono);
|
|
1513
|
+
overflow-wrap: anywhere;
|
|
1500
1514
|
font-size: var(--ef-type-size-label);
|
|
1501
1515
|
line-height: var(--ef-type-leading-snug);
|
|
1502
1516
|
text-decoration: none;
|
|
@@ -1542,7 +1556,15 @@
|
|
|
1542
1556
|
min-inline-size: 0;
|
|
1543
1557
|
padding: var(--ef-layout-block-space);
|
|
1544
1558
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
1545
|
-
|
|
1559
|
+
/*
|
|
1560
|
+
* A brand radius is capped by the padding (LCE-001). Content whose corner
|
|
1561
|
+
* sits at padding p from both edges stays clear of a circular corner of
|
|
1562
|
+
* radius r while r <= (2 + √2)·(p - c) + c for clearance c; with c = 1px
|
|
1563
|
+
* that is p·3.414 - 2.414px. A badge or button uses its own two paddings:
|
|
1564
|
+
* pi + pb·2.414 - 2.414px (pb the smaller). A larger brand radius (a pill
|
|
1565
|
+
* or 50%) would otherwise cut through multi-line content.
|
|
1566
|
+
*/
|
|
1567
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-layout-block-space) * 3.414 - 2.414px));
|
|
1546
1568
|
}
|
|
1547
1569
|
|
|
1548
1570
|
/* Inside a grid, cards share hairline rules instead of doubling borders. */
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"forma": "0.
|
|
3
|
+
"forma": "0.10.0",
|
|
4
4
|
"bundle": "echelon-marketing",
|
|
5
5
|
"description": "Forma presentation artifacts for Echelon marketing sites. Every artifact is a single self-contained CSS file with no imports or relative URLs.",
|
|
6
|
-
"releaseTag": "v0.
|
|
6
|
+
"releaseTag": "v0.10.0",
|
|
7
7
|
"artifacts": [
|
|
8
8
|
{
|
|
9
9
|
"file": "forma-marketing.css",
|
|
10
10
|
"description": "Brand-neutral marketing layer: core tokens, role defaults, Forma foundations, marketing foundations, components, and layouts.",
|
|
11
|
-
"bytes":
|
|
12
|
-
"sha256": "
|
|
11
|
+
"bytes": 87243,
|
|
12
|
+
"sha256": "d8a900aecd8aa4a72cbda731d3bbcfadbf872bc8aed1a8ef74f5ef3169af4cc6",
|
|
13
13
|
"sources": [
|
|
14
14
|
"dist/tokens.css",
|
|
15
15
|
"src/marketing/roles.css",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
{
|
|
23
23
|
"file": "forma-marketing-theme-echelon.css",
|
|
24
24
|
"description": "Echelon Marketing Theme: compiled theme tokens and theme hooks. Load after forma-marketing.css.",
|
|
25
|
-
"bytes":
|
|
26
|
-
"sha256": "
|
|
25
|
+
"bytes": 6296,
|
|
26
|
+
"sha256": "e3e05cf66b87ac6be686896c353ab87f47ad1be589c1fc2895238d1bd987d9e6",
|
|
27
27
|
"sources": [
|
|
28
28
|
"build/marketing/echelon-marketing.tokens.css",
|
|
29
29
|
"themes/echelon/marketing.css"
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
{
|
|
33
33
|
"file": "forma-echelon-marketing.css",
|
|
34
34
|
"description": "Single-file Echelon marketing presentation: forma-marketing.css followed by the Echelon theme. Most sites load only this file.",
|
|
35
|
-
"bytes":
|
|
36
|
-
"sha256": "
|
|
35
|
+
"bytes": 93383,
|
|
36
|
+
"sha256": "837ddc667b3eccbc6a0512fe767708212a2aeffe18f9c2a27cbedad9be2937f3",
|
|
37
37
|
"sources": [
|
|
38
38
|
"artifact:forma-marketing.css",
|
|
39
39
|
"artifact:forma-marketing-theme-echelon.css"
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
{
|
|
43
43
|
"file": "forma-all.css",
|
|
44
44
|
"description": "Complete Forma surface (application components, assessment, and marketing layers) for sites that also present Forma application patterns such as data grids or status lozenges.",
|
|
45
|
-
"bytes":
|
|
46
|
-
"sha256": "
|
|
45
|
+
"bytes": 336745,
|
|
46
|
+
"sha256": "6666ea17b441934dd66807f9aefae86213ca228c92eed8da17faed2b2d816b18",
|
|
47
47
|
"sources": [
|
|
48
48
|
"dist/all.css"
|
|
49
49
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
d8a900aecd8aa4a72cbda731d3bbcfadbf872bc8aed1a8ef74f5ef3169af4cc6 forma-marketing.css
|
|
2
|
+
e3e05cf66b87ac6be686896c353ab87f47ad1be589c1fc2895238d1bd987d9e6 forma-marketing-theme-echelon.css
|
|
3
|
+
837ddc667b3eccbc6a0512fe767708212a2aeffe18f9c2a27cbedad9be2937f3 forma-echelon-marketing.css
|
|
4
|
+
6666ea17b441934dd66807f9aefae86213ca228c92eed8da17faed2b2d816b18 forma-all.css
|
package/dist/marketing.css
CHANGED
|
@@ -218,6 +218,14 @@
|
|
|
218
218
|
opacity: var(--ef-site-backdrop-opacity, 1);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/*
|
|
222
|
+
* Prose defaults stop at an embedded diagram (LCE-001). Diagram geometry is
|
|
223
|
+
* computed for the component layer's type metrics; a site heading size or
|
|
224
|
+
* paragraph margin made nodes outgrow their layout. revert-layer hands these
|
|
225
|
+
* elements back to the earlier layers.
|
|
226
|
+
*/
|
|
227
|
+
.ef-site .ef-diagram :where(h1, h2, h3, h4, h5, h6, p, ul, ol, li, a, svg, dl, dt, dd, strong, b) { all: revert-layer; }
|
|
228
|
+
|
|
221
229
|
.ef-site :where(h1, h2, h3, h4, h5, h6) {
|
|
222
230
|
margin-block: 0;
|
|
223
231
|
color: var(--ef-tone-heading);
|
|
@@ -516,7 +524,8 @@
|
|
|
516
524
|
min-block-size: calc(var(--ef-layout-target-min) + var(--ef-space-2));
|
|
517
525
|
padding: var(--ef-space-3) var(--ef-space-5);
|
|
518
526
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule-strong);
|
|
519
|
-
|
|
527
|
+
/* Bounded so a brand radius never reaches the content (LCE-001, see .ef-card). */
|
|
528
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-5) + var(--ef-space-3) * 2.414 - 2.414px));
|
|
520
529
|
background: transparent;
|
|
521
530
|
color: var(--ef-tone-text);
|
|
522
531
|
font: inherit;
|
|
@@ -528,7 +537,10 @@
|
|
|
528
537
|
/* Never wider than its row and never split mid-word unless one word is
|
|
529
538
|
wider than the row (ULI-L05, L06). */
|
|
530
539
|
max-inline-size: 100%;
|
|
531
|
-
|
|
540
|
+
/* anywhere, not break-word: inside inline-flex only anywhere lets the
|
|
541
|
+
min-content width shrink, so a word wider than the row breaks instead of
|
|
542
|
+
overflowing the button (LCE-001). Words still break only when they must. */
|
|
543
|
+
overflow-wrap: anywhere;
|
|
532
544
|
cursor: pointer;
|
|
533
545
|
/* Hover and press are perceptual emphasis on the surface only: the hit
|
|
534
546
|
target never moves under the pointer (MOT-014). */
|
|
@@ -573,11 +585,13 @@
|
|
|
573
585
|
display: inline-flex;
|
|
574
586
|
align-items: center;
|
|
575
587
|
gap: var(--ef-space-1);
|
|
588
|
+
max-inline-size: 100%;
|
|
576
589
|
padding: var(--ef-space-1) var(--ef-space-2);
|
|
577
590
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
578
|
-
border-radius: var(--ef-shape-radius);
|
|
591
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-space-2) + var(--ef-space-1) * 2.414 - 2.414px));
|
|
579
592
|
color: var(--ef-tone-text);
|
|
580
593
|
font-family: var(--ef-type-family-mono);
|
|
594
|
+
overflow-wrap: anywhere;
|
|
581
595
|
font-size: var(--ef-type-size-label);
|
|
582
596
|
line-height: var(--ef-type-leading-snug);
|
|
583
597
|
text-decoration: none;
|
|
@@ -623,7 +637,15 @@
|
|
|
623
637
|
min-inline-size: 0;
|
|
624
638
|
padding: var(--ef-layout-block-space);
|
|
625
639
|
border: var(--ef-shape-border-width) solid var(--ef-tone-rule);
|
|
626
|
-
|
|
640
|
+
/*
|
|
641
|
+
* A brand radius is capped by the padding (LCE-001). Content whose corner
|
|
642
|
+
* sits at padding p from both edges stays clear of a circular corner of
|
|
643
|
+
* radius r while r <= (2 + √2)·(p - c) + c for clearance c; with c = 1px
|
|
644
|
+
* that is p·3.414 - 2.414px. A badge or button uses its own two paddings:
|
|
645
|
+
* pi + pb·2.414 - 2.414px (pb the smaller). A larger brand radius (a pill
|
|
646
|
+
* or 50%) would otherwise cut through multi-line content.
|
|
647
|
+
*/
|
|
648
|
+
border-radius: min(var(--ef-shape-radius), calc(var(--ef-layout-block-space) * 3.414 - 2.414px));
|
|
627
649
|
}
|
|
628
650
|
|
|
629
651
|
/* Inside a grid, cards share hairline rules instead of doubling borders. */
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Generated by tools/build-runtime.mjs from package.json. Do not edit.
|
|
2
|
+
const SCHEMA_VERSION = 1;
|
|
3
|
+
const REGISTRY_KEY = "__ECHELON_RUNTIME__";
|
|
4
|
+
const REGISTER_KEY = "__ECHELON_REGISTER_RUNTIME__";
|
|
5
|
+
|
|
6
|
+
const libraryLabel = name => name.split("-").map(part => part ? part[0].toUpperCase() + part.slice(1) : part).join(" ");
|
|
7
|
+
const diagnostic = (target, method, message) => target.console?.[method]?.(message);
|
|
8
|
+
|
|
9
|
+
export function installEchelonRuntimeRegistrar(target = globalThis) {
|
|
10
|
+
const installed = target[REGISTER_KEY];
|
|
11
|
+
if (typeof installed === "function" && installed.echelonRuntimeSchemaVersion === SCHEMA_VERSION) return installed;
|
|
12
|
+
if (installed !== undefined) {
|
|
13
|
+
diagnostic(target, "warn", "Echelon Runtime registrar conflict: an incompatible global registrar is already installed.");
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let registry = target[REGISTRY_KEY];
|
|
18
|
+
if (registry === undefined) {
|
|
19
|
+
registry = { schemaVersion: SCHEMA_VERSION, libraries: {} };
|
|
20
|
+
target[REGISTRY_KEY] = registry;
|
|
21
|
+
} else if (registry === null || typeof registry !== "object" || registry.schemaVersion !== SCHEMA_VERSION || registry.libraries === null || typeof registry.libraries !== "object") {
|
|
22
|
+
diagnostic(target, "warn", "Echelon Runtime registry conflict: schemaVersion 1 is required; the existing registry was left unchanged.");
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const register = (name, metadata) => {
|
|
27
|
+
if (typeof name !== "string" || !/^[a-z][a-z0-9-]*$/.test(name)) throw new TypeError("Echelon runtime library names must be lowercase identifiers.");
|
|
28
|
+
if (metadata === null || typeof metadata !== "object" || typeof metadata.version !== "string" || metadata.version.length === 0) throw new TypeError("Echelon runtime metadata requires a version string.");
|
|
29
|
+
|
|
30
|
+
const candidate = { version: metadata.version };
|
|
31
|
+
if (typeof metadata.commit === "string" && metadata.commit.length > 0) candidate.commit = metadata.commit;
|
|
32
|
+
if (typeof metadata.buildTime === "string" && metadata.buildTime.length > 0) candidate.buildTime = metadata.buildTime;
|
|
33
|
+
|
|
34
|
+
const current = registry.libraries[name];
|
|
35
|
+
if (current !== undefined) {
|
|
36
|
+
if (current.version === candidate.version) return current;
|
|
37
|
+
diagnostic(target, "warn", `Echelon Runtime conflict: ${libraryLabel(name)} v${current.version} is already registered; ignored v${candidate.version}.`);
|
|
38
|
+
return current;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
registry.libraries[name] = candidate;
|
|
42
|
+
diagnostic(target, "info", `Echelon Runtime · ${libraryLabel(name)} v${candidate.version}`);
|
|
43
|
+
return candidate;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
Object.defineProperty(register, "echelonRuntimeSchemaVersion", { value: SCHEMA_VERSION });
|
|
47
|
+
Object.defineProperty(target, REGISTER_KEY, { value: register, configurable: true });
|
|
48
|
+
return register;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function registerEchelonRuntime(name, metadata, target = globalThis) {
|
|
52
|
+
return installEchelonRuntimeRegistrar(target)?.(name, metadata);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const FORMA_VERSION = "0.10.0";
|
|
56
|
+
|
|
57
|
+
if (typeof window !== "undefined") registerEchelonRuntime("forma", { version: FORMA_VERSION }, window);
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@echelon-foundry/design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-main.257.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
|
-
"description": "Zero-runtime HTML and CSS design system for Echelon Foundry applications.",
|
|
6
|
+
"description": "Zero-component-runtime HTML and CSS design system for Echelon Foundry applications.",
|
|
7
7
|
"sideEffects": [
|
|
8
|
+
"./dist/runtime.js",
|
|
8
9
|
"./dist/*.css",
|
|
9
10
|
"./dist/brands/*.css",
|
|
10
11
|
"./dist/marketing.css",
|
|
11
12
|
"./dist/marketing/*.css"
|
|
12
13
|
],
|
|
13
14
|
"exports": {
|
|
15
|
+
"./runtime": "./dist/runtime.js",
|
|
14
16
|
"./tokens.css": "./dist/tokens.css",
|
|
15
17
|
"./foundations.css": "./dist/foundations.css",
|
|
16
18
|
"./components.css": "./dist/components.css",
|
|
@@ -67,7 +69,8 @@
|
|
|
67
69
|
"clean": "node tools/clean.mjs",
|
|
68
70
|
"tokens:build": "dotnet run --project tools/TokenCompiler/TokenCompiler.fsproj -- tokens/echelon.tokens.json dist/tokens.css",
|
|
69
71
|
"assets:build": "node tools/build-assets.mjs",
|
|
70
|
-
"build": "npm run clean && npm run tokens:build && npm run brands:build && npm run color:build && npm run marketing:theme && npm run assets:build && npm run marketing:build && npm run example:install",
|
|
72
|
+
"build": "npm run clean && npm run tokens:build && npm run brands:build && npm run color:build && npm run marketing:theme && npm run runtime:build && npm run assets:build && npm run marketing:build && npm run example:install",
|
|
73
|
+
"runtime:build": "node tools/build-runtime.mjs",
|
|
71
74
|
"test:tokens": "node --test tests/token-compiler.test.mjs",
|
|
72
75
|
"test:browser": "playwright test",
|
|
73
76
|
"test:a11y": "playwright test tests/browser/accessibility.spec.mjs",
|
|
@@ -99,6 +102,8 @@
|
|
|
99
102
|
"catalog:check": "node tools/catalog/validate.mjs && node tools/catalog/inventory.mjs --check",
|
|
100
103
|
"catalog:inventory": "node tools/catalog/inventory.mjs",
|
|
101
104
|
"workflow:test": "dotnet run --project tests/workflow/Forma.Workflow.Tests -c Release",
|
|
105
|
+
"layout:probe": "dotnet run --project tests/layout/Forma.Layout.Probe -c Release --",
|
|
106
|
+
"test:layout": "dotnet run --project tests/layout/Forma.Layout.Probe -c Release -- regress",
|
|
102
107
|
"workflow:build": "node tools/build-workflow-package.mjs",
|
|
103
108
|
"workflow:validate": "dotnet run --project src/workflow/Forma.Workflow.Cli -c Release -- validate examples/workflows/forma/workflows/*.forma-workflow.json",
|
|
104
109
|
"design:validate": "node tools/design-contract/cli.mjs validate examples/design/valid/*.forma-design.json",
|
|
@@ -127,7 +132,7 @@
|
|
|
127
132
|
"homepage": "https://forma.echelonfoundry.com/",
|
|
128
133
|
"keywords": [
|
|
129
134
|
"design-system",
|
|
130
|
-
"zero-runtime",
|
|
135
|
+
"zero-component-runtime",
|
|
131
136
|
"marketing-presentation",
|
|
132
137
|
"accessibility",
|
|
133
138
|
"workflow",
|