@echelon-foundry/design-system 0.6.1 → 0.7.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.
@@ -1,4 +1,4 @@
1
- /*! Forma 0.6.1 | forma-echelon-marketing.css | bundle echelon-marketing
1
+ /*! Forma 0.7.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
 
@@ -737,6 +737,7 @@
737
737
  }
738
738
 
739
739
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
740
+ box-sizing: border-box;
740
741
  min-block-size: 2.75rem;
741
742
  inline-size: 100%;
742
743
  padding: 0.7rem 0.8rem;
@@ -744,6 +745,50 @@
744
745
  border-radius: var(--ef-primitive-radius-none, 0);
745
746
  background: var(--ef-color-surface-primary, #f2efe7);
746
747
  color: var(--ef-color-text-primary, #171a18);
748
+ font: inherit;
749
+ line-height: var(--ef-field-line-height, 1.6);
750
+ }
751
+
752
+ /*
753
+ * Equivalent single-line controls share one intrinsic block size
754
+ * (UI-LAYOUT-INTERACTION ULI-L02, L13): one line of field text plus the
755
+ * field padding and border. Native date/time and select boxes otherwise
756
+ * differ by several pixels from text inputs (Chromium: 51.97 vs 49.97 vs
757
+ * 46.38 px), which staggers peers in a row. Em-based, so it grows with
758
+ * the user's text size.
759
+ */
760
+ :where(
761
+ input:not([type]),
762
+ input:is([type="text"], [type="email"], [type="password"], [type="search"], [type="tel"], [type="url"], [type="number"],
763
+ [type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]),
764
+ select:not([multiple], [size])
765
+ ) {
766
+ block-size: var(--ef-field-control-block-size, calc(var(--ef-field-line-height, 1.6) * 1em + 1.4rem + 2px));
767
+ }
768
+
769
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"])::-webkit-datetime-edit {
770
+ padding-block: 0;
771
+ }
772
+
773
+ /* A fieldset's default min-inline-size is min-content, which lets a long
774
+ legend or option widen a form past a narrow viewport (ULI-L05). */
775
+ :where(fieldset) {
776
+ min-inline-size: 0;
777
+ }
778
+
779
+ /* iOS temporal controls otherwise retain a native intrinsic width despite
780
+ the author's width limit. Keep the native picker, not its native box. */
781
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"]) {
782
+ -webkit-appearance: none;
783
+ appearance: none;
784
+ box-sizing: border-box;
785
+ min-inline-size: 0;
786
+ max-inline-size: 100%;
787
+ }
788
+
789
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"])::-webkit-date-and-time-value {
790
+ min-inline-size: 0;
791
+ text-align: start;
747
792
  }
748
793
 
749
794
  :where(:focus-visible) {
@@ -755,16 +800,29 @@
755
800
  .ef-stack { display: flex; flex-direction: column; gap: var(--ef-stack-gap, var(--ef-primitive-spacing-4, 1rem)); }
756
801
  .ef-cluster { display: flex; flex-wrap: wrap; gap: var(--ef-cluster-gap, var(--ef-primitive-spacing-3, 0.75rem)); align-items: center; }
757
802
  /* .ef-actions (catalog: button) is an application-layer row of related actions; it was styled only in marketing.css (forma#93 gap). */
758
- .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; }
803
+ /* min-inline-size: 0 lets an action row shrink inside a flex or grid parent
804
+ instead of being held at its widest button's word (ULI-L05). */
805
+ .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; min-inline-size: 0; }
759
806
  .ef-grid { display: grid; gap: var(--ef-grid-gap, var(--ef-primitive-spacing-4, 1rem)); grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-grid-min, 16rem)), 1fr)); }
760
807
  .ef-section { padding-block: var(--ef-section-space, var(--ef-primitive-spacing-7, 3rem)); }
761
808
  .ef-surface { --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18); min-inline-size: 0; padding: var(--ef-surface-space, var(--ef-primitive-spacing-5, 1.5rem)); background: var(--ef-color-surface-secondary, #e3e0d7); color: var(--ef-color-text-on-secondary-surface, #3a403c); border: 1px solid var(--ef-color-border-subtle, #e3e0d7); }
762
809
  .ef-surface :where(h1, h2, h3, h4, h5, h6, p) { color: inherit; }
763
810
 
764
- :where(h1, h2, h3, h4, h5, h6, p, a, label, legend, td, th) {
811
+ :where(h1, h2, h3, h4, h5, h6, p, a, label, legend, td, th, dt, dd, caption, figcaption) {
765
812
  overflow-wrap: anywhere;
766
813
  }
767
814
 
815
+ /* Action labels never split mid-word because their container allows it
816
+ (a link button inherits the rule above; a button inherits it from a
817
+ table cell). A single word wider than its container may still break
818
+ rather than overflow. Must follow the rule above (ULI-L06, L07). The
819
+ inline-size cap also clamps a flex item's automatic minimum, so a
820
+ button never pushes its row past its container (ULI-L05). */
821
+ :where(button, .ef-button) {
822
+ overflow-wrap: break-word;
823
+ max-inline-size: 100%;
824
+ }
825
+
768
826
  @media (prefers-reduced-motion: reduce) {
769
827
  *, *::before, *::after {
770
828
  scroll-behavior: auto !important;
@@ -1261,6 +1319,9 @@
1261
1319
  flex-wrap: wrap;
1262
1320
  align-items: center;
1263
1321
  gap: var(--ef-space-3);
1322
+ /* Shrinks inside a flex or grid parent instead of being held at its
1323
+ widest button's word (ULI-L05). */
1324
+ min-inline-size: 0;
1264
1325
  }
1265
1326
 
1266
1327
  .ef-site .ef-button {
@@ -1280,6 +1341,10 @@
1280
1341
  line-height: var(--ef-type-leading-snug);
1281
1342
  text-align: center;
1282
1343
  text-decoration: none;
1344
+ /* Never wider than its row and never split mid-word unless one word is
1345
+ wider than the row (ULI-L05, L06). */
1346
+ max-inline-size: 100%;
1347
+ overflow-wrap: break-word;
1283
1348
  cursor: pointer;
1284
1349
  /* Hover and press are perceptual emphasis on the surface only: the hit
1285
1350
  target never moves under the pointer (MOT-014). */
@@ -1526,7 +1591,10 @@
1526
1591
  min-block-size: 1.5rem;
1527
1592
  font-size: var(--ef-type-size-small);
1528
1593
  font-weight: var(--ef-type-weight-strong);
1529
- white-space: nowrap;
1594
+ /* One line whenever it fits (inline-flex shrinks to its content); it
1595
+ wraps at word boundaries instead of overflowing a narrow column at
1596
+ large text sizes (UI-LAYOUT-INTERACTION ULI-L05). */
1597
+ max-inline-size: 100%;
1530
1598
  }
1531
1599
 
1532
1600
  /* Four disparate columns cannot degrade by a minimum item size, so the
@@ -1558,6 +1626,9 @@
1558
1626
  padding-block: var(--ef-space-4);
1559
1627
  border-block-end: var(--ef-shape-border-width) solid var(--ef-tone-rule);
1560
1628
  color: var(--ef-tone-text);
1629
+ /* The step text is an anonymous flex item; let a long word wrap inside
1630
+ it instead of overflowing a narrow column (ULI-L05). */
1631
+ overflow-wrap: anywhere;
1561
1632
  }
1562
1633
 
1563
1634
  .ef-steps > li::before {
@@ -1687,6 +1758,7 @@
1687
1758
 
1688
1759
  .ef-code-figure > figcaption {
1689
1760
  margin-block-end: var(--ef-space-2);
1761
+ overflow-wrap: anywhere;
1690
1762
  color: var(--ef-tone-muted);
1691
1763
  font-family: var(--ef-type-family-mono);
1692
1764
  font-size: var(--ef-type-size-label);
@@ -1828,6 +1900,11 @@
1828
1900
  .ef-site .ef-section[data-ef-tone]:not([data-ef-tone="page"]) {
1829
1901
  margin-block: calc(var(--ef-layout-section-space) / 2);
1830
1902
  padding: calc(var(--ef-layout-block-space) * 2);
1903
+ /* The block-space token is rem-based; at enlarged text on a phone it
1904
+ would leave a few characters of line length (96px content at 320px
1905
+ and 200% text). Cap only the inline padding by viewport width; this
1906
+ changes nothing at the default text size (ULI-L05). */
1907
+ padding-inline: min(calc(var(--ef-layout-block-space) * 2), 12.5vw);
1831
1908
  }
1832
1909
 
1833
1910
  .ef-section > * + * { margin-block-start: var(--ef-layout-stack-space); }
@@ -1,4 +1,4 @@
1
- /*! Forma 0.6.1 | forma-marketing-theme-echelon.css | bundle echelon-marketing
1
+ /*! Forma 0.7.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.6.1 | forma-marketing.css | bundle echelon-marketing
1
+ /*! Forma 0.7.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
 
@@ -736,6 +736,7 @@
736
736
  }
737
737
 
738
738
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
739
+ box-sizing: border-box;
739
740
  min-block-size: 2.75rem;
740
741
  inline-size: 100%;
741
742
  padding: 0.7rem 0.8rem;
@@ -743,6 +744,50 @@
743
744
  border-radius: var(--ef-primitive-radius-none, 0);
744
745
  background: var(--ef-color-surface-primary, #f2efe7);
745
746
  color: var(--ef-color-text-primary, #171a18);
747
+ font: inherit;
748
+ line-height: var(--ef-field-line-height, 1.6);
749
+ }
750
+
751
+ /*
752
+ * Equivalent single-line controls share one intrinsic block size
753
+ * (UI-LAYOUT-INTERACTION ULI-L02, L13): one line of field text plus the
754
+ * field padding and border. Native date/time and select boxes otherwise
755
+ * differ by several pixels from text inputs (Chromium: 51.97 vs 49.97 vs
756
+ * 46.38 px), which staggers peers in a row. Em-based, so it grows with
757
+ * the user's text size.
758
+ */
759
+ :where(
760
+ input:not([type]),
761
+ input:is([type="text"], [type="email"], [type="password"], [type="search"], [type="tel"], [type="url"], [type="number"],
762
+ [type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]),
763
+ select:not([multiple], [size])
764
+ ) {
765
+ block-size: var(--ef-field-control-block-size, calc(var(--ef-field-line-height, 1.6) * 1em + 1.4rem + 2px));
766
+ }
767
+
768
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"])::-webkit-datetime-edit {
769
+ padding-block: 0;
770
+ }
771
+
772
+ /* A fieldset's default min-inline-size is min-content, which lets a long
773
+ legend or option widen a form past a narrow viewport (ULI-L05). */
774
+ :where(fieldset) {
775
+ min-inline-size: 0;
776
+ }
777
+
778
+ /* iOS temporal controls otherwise retain a native intrinsic width despite
779
+ the author's width limit. Keep the native picker, not its native box. */
780
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"]) {
781
+ -webkit-appearance: none;
782
+ appearance: none;
783
+ box-sizing: border-box;
784
+ min-inline-size: 0;
785
+ max-inline-size: 100%;
786
+ }
787
+
788
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"])::-webkit-date-and-time-value {
789
+ min-inline-size: 0;
790
+ text-align: start;
746
791
  }
747
792
 
748
793
  :where(:focus-visible) {
@@ -754,16 +799,29 @@
754
799
  .ef-stack { display: flex; flex-direction: column; gap: var(--ef-stack-gap, var(--ef-primitive-spacing-4, 1rem)); }
755
800
  .ef-cluster { display: flex; flex-wrap: wrap; gap: var(--ef-cluster-gap, var(--ef-primitive-spacing-3, 0.75rem)); align-items: center; }
756
801
  /* .ef-actions (catalog: button) is an application-layer row of related actions; it was styled only in marketing.css (forma#93 gap). */
757
- .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; }
802
+ /* min-inline-size: 0 lets an action row shrink inside a flex or grid parent
803
+ instead of being held at its widest button's word (ULI-L05). */
804
+ .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; min-inline-size: 0; }
758
805
  .ef-grid { display: grid; gap: var(--ef-grid-gap, var(--ef-primitive-spacing-4, 1rem)); grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-grid-min, 16rem)), 1fr)); }
759
806
  .ef-section { padding-block: var(--ef-section-space, var(--ef-primitive-spacing-7, 3rem)); }
760
807
  .ef-surface { --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18); min-inline-size: 0; padding: var(--ef-surface-space, var(--ef-primitive-spacing-5, 1.5rem)); background: var(--ef-color-surface-secondary, #e3e0d7); color: var(--ef-color-text-on-secondary-surface, #3a403c); border: 1px solid var(--ef-color-border-subtle, #e3e0d7); }
761
808
  .ef-surface :where(h1, h2, h3, h4, h5, h6, p) { color: inherit; }
762
809
 
763
- :where(h1, h2, h3, h4, h5, h6, p, a, label, legend, td, th) {
810
+ :where(h1, h2, h3, h4, h5, h6, p, a, label, legend, td, th, dt, dd, caption, figcaption) {
764
811
  overflow-wrap: anywhere;
765
812
  }
766
813
 
814
+ /* Action labels never split mid-word because their container allows it
815
+ (a link button inherits the rule above; a button inherits it from a
816
+ table cell). A single word wider than its container may still break
817
+ rather than overflow. Must follow the rule above (ULI-L06, L07). The
818
+ inline-size cap also clamps a flex item's automatic minimum, so a
819
+ button never pushes its row past its container (ULI-L05). */
820
+ :where(button, .ef-button) {
821
+ overflow-wrap: break-word;
822
+ max-inline-size: 100%;
823
+ }
824
+
767
825
  @media (prefers-reduced-motion: reduce) {
768
826
  *, *::before, *::after {
769
827
  scroll-behavior: auto !important;
@@ -1260,6 +1318,9 @@
1260
1318
  flex-wrap: wrap;
1261
1319
  align-items: center;
1262
1320
  gap: var(--ef-space-3);
1321
+ /* Shrinks inside a flex or grid parent instead of being held at its
1322
+ widest button's word (ULI-L05). */
1323
+ min-inline-size: 0;
1263
1324
  }
1264
1325
 
1265
1326
  .ef-site .ef-button {
@@ -1279,6 +1340,10 @@
1279
1340
  line-height: var(--ef-type-leading-snug);
1280
1341
  text-align: center;
1281
1342
  text-decoration: none;
1343
+ /* Never wider than its row and never split mid-word unless one word is
1344
+ wider than the row (ULI-L05, L06). */
1345
+ max-inline-size: 100%;
1346
+ overflow-wrap: break-word;
1282
1347
  cursor: pointer;
1283
1348
  /* Hover and press are perceptual emphasis on the surface only: the hit
1284
1349
  target never moves under the pointer (MOT-014). */
@@ -1525,7 +1590,10 @@
1525
1590
  min-block-size: 1.5rem;
1526
1591
  font-size: var(--ef-type-size-small);
1527
1592
  font-weight: var(--ef-type-weight-strong);
1528
- white-space: nowrap;
1593
+ /* One line whenever it fits (inline-flex shrinks to its content); it
1594
+ wraps at word boundaries instead of overflowing a narrow column at
1595
+ large text sizes (UI-LAYOUT-INTERACTION ULI-L05). */
1596
+ max-inline-size: 100%;
1529
1597
  }
1530
1598
 
1531
1599
  /* Four disparate columns cannot degrade by a minimum item size, so the
@@ -1557,6 +1625,9 @@
1557
1625
  padding-block: var(--ef-space-4);
1558
1626
  border-block-end: var(--ef-shape-border-width) solid var(--ef-tone-rule);
1559
1627
  color: var(--ef-tone-text);
1628
+ /* The step text is an anonymous flex item; let a long word wrap inside
1629
+ it instead of overflowing a narrow column (ULI-L05). */
1630
+ overflow-wrap: anywhere;
1560
1631
  }
1561
1632
 
1562
1633
  .ef-steps > li::before {
@@ -1686,6 +1757,7 @@
1686
1757
 
1687
1758
  .ef-code-figure > figcaption {
1688
1759
  margin-block-end: var(--ef-space-2);
1760
+ overflow-wrap: anywhere;
1689
1761
  color: var(--ef-tone-muted);
1690
1762
  font-family: var(--ef-type-family-mono);
1691
1763
  font-size: var(--ef-type-size-label);
@@ -1827,6 +1899,11 @@
1827
1899
  .ef-site .ef-section[data-ef-tone]:not([data-ef-tone="page"]) {
1828
1900
  margin-block: calc(var(--ef-layout-section-space) / 2);
1829
1901
  padding: calc(var(--ef-layout-block-space) * 2);
1902
+ /* The block-space token is rem-based; at enlarged text on a phone it
1903
+ would leave a few characters of line length (96px content at 320px
1904
+ and 200% text). Cap only the inline padding by viewport width; this
1905
+ changes nothing at the default text size (ULI-L05). */
1906
+ padding-inline: min(calc(var(--ef-layout-block-space) * 2), 12.5vw);
1830
1907
  }
1831
1908
 
1832
1909
  .ef-section > * + * { margin-block-start: var(--ef-layout-stack-space); }
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "forma": "0.6.1",
3
+ "forma": "0.7.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.1",
6
+ "releaseTag": "v0.7.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": 78117,
12
- "sha256": "d2dd5cab7ebba2c2d667fa6cfab18bf3b19de729469e2fcca07a58d27c011525",
11
+ "bytes": 81740,
12
+ "sha256": "171d3cb1485da7a9f83f6bdfc80c8043b0d194a32eadc3e0b5e09b12fade2f80",
13
13
  "sources": [
14
14
  "dist/tokens.css",
15
15
  "src/marketing/roles.css",
@@ -23,7 +23,7 @@
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
25
  "bytes": 6295,
26
- "sha256": "33a128ec9b1fd5081a6d8ed37ca641a05ec9e91eb342026c34b5bed69f5d750b",
26
+ "sha256": "027334d7946a3342812dca297967630cb5f5b1e96dd849672c8e146bf257b153",
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": 84257,
36
- "sha256": "6f89190f3133a0cad35b05d3b00c9e17f2036e3c8021347b0dcd7bad50ac5af5",
35
+ "bytes": 87880,
36
+ "sha256": "2baacb6423cc930e15b1da5cbf39289bed5bd3c721e35ef2e97cb73f48c0c0fc",
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": 317863,
46
- "sha256": "3c129b05e8e6049e3e7d61bb09216640f1cae504689021b4262145000c37be01",
45
+ "bytes": 327293,
46
+ "sha256": "5aae64c6fa2e8784347b2207fe7324dae2492a5f45f6b134463e243f419b4dec",
47
47
  "sources": [
48
48
  "dist/all.css"
49
49
  ]
@@ -1,4 +1,4 @@
1
- d2dd5cab7ebba2c2d667fa6cfab18bf3b19de729469e2fcca07a58d27c011525 forma-marketing.css
2
- 33a128ec9b1fd5081a6d8ed37ca641a05ec9e91eb342026c34b5bed69f5d750b forma-marketing-theme-echelon.css
3
- 6f89190f3133a0cad35b05d3b00c9e17f2036e3c8021347b0dcd7bad50ac5af5 forma-echelon-marketing.css
4
- 3c129b05e8e6049e3e7d61bb09216640f1cae504689021b4262145000c37be01 forma-all.css
1
+ 171d3cb1485da7a9f83f6bdfc80c8043b0d194a32eadc3e0b5e09b12fade2f80 forma-marketing.css
2
+ 027334d7946a3342812dca297967630cb5f5b1e96dd849672c8e146bf257b153 forma-marketing-theme-echelon.css
3
+ 2baacb6423cc930e15b1da5cbf39289bed5bd3c721e35ef2e97cb73f48c0c0fc forma-echelon-marketing.css
4
+ 5aae64c6fa2e8784347b2207fe7324dae2492a5f45f6b134463e243f419b4dec forma-all.css
@@ -503,6 +503,9 @@
503
503
  flex-wrap: wrap;
504
504
  align-items: center;
505
505
  gap: var(--ef-space-3);
506
+ /* Shrinks inside a flex or grid parent instead of being held at its
507
+ widest button's word (ULI-L05). */
508
+ min-inline-size: 0;
506
509
  }
507
510
 
508
511
  .ef-site .ef-button {
@@ -522,6 +525,10 @@
522
525
  line-height: var(--ef-type-leading-snug);
523
526
  text-align: center;
524
527
  text-decoration: none;
528
+ /* Never wider than its row and never split mid-word unless one word is
529
+ wider than the row (ULI-L05, L06). */
530
+ max-inline-size: 100%;
531
+ overflow-wrap: break-word;
525
532
  cursor: pointer;
526
533
  /* Hover and press are perceptual emphasis on the surface only: the hit
527
534
  target never moves under the pointer (MOT-014). */
@@ -768,7 +775,10 @@
768
775
  min-block-size: 1.5rem;
769
776
  font-size: var(--ef-type-size-small);
770
777
  font-weight: var(--ef-type-weight-strong);
771
- white-space: nowrap;
778
+ /* One line whenever it fits (inline-flex shrinks to its content); it
779
+ wraps at word boundaries instead of overflowing a narrow column at
780
+ large text sizes (UI-LAYOUT-INTERACTION ULI-L05). */
781
+ max-inline-size: 100%;
772
782
  }
773
783
 
774
784
  /* Four disparate columns cannot degrade by a minimum item size, so the
@@ -800,6 +810,9 @@
800
810
  padding-block: var(--ef-space-4);
801
811
  border-block-end: var(--ef-shape-border-width) solid var(--ef-tone-rule);
802
812
  color: var(--ef-tone-text);
813
+ /* The step text is an anonymous flex item; let a long word wrap inside
814
+ it instead of overflowing a narrow column (ULI-L05). */
815
+ overflow-wrap: anywhere;
803
816
  }
804
817
 
805
818
  .ef-steps > li::before {
@@ -929,6 +942,7 @@
929
942
 
930
943
  .ef-code-figure > figcaption {
931
944
  margin-block-end: var(--ef-space-2);
945
+ overflow-wrap: anywhere;
932
946
  color: var(--ef-tone-muted);
933
947
  font-family: var(--ef-type-family-mono);
934
948
  font-size: var(--ef-type-size-label);
@@ -1069,6 +1083,11 @@
1069
1083
  .ef-site .ef-section[data-ef-tone]:not([data-ef-tone="page"]) {
1070
1084
  margin-block: calc(var(--ef-layout-section-space) / 2);
1071
1085
  padding: calc(var(--ef-layout-block-space) * 2);
1086
+ /* The block-space token is rem-based; at enlarged text on a phone it
1087
+ would leave a few characters of line length (96px content at 320px
1088
+ and 200% text). Cap only the inline padding by viewport width; this
1089
+ changes nothing at the default text size (ULI-L05). */
1090
+ padding-inline: min(calc(var(--ef-layout-block-space) * 2), 12.5vw);
1072
1091
  }
1073
1092
 
1074
1093
  .ef-section > * + * { margin-block-start: var(--ef-layout-stack-space); }
@@ -0,0 +1,15 @@
1
+ <div class="ef-form-row">
2
+ <div class="ef-field">
3
+ <label class="ef-field__label" for="form-row-given-name">Given name</label>
4
+ <input id="form-row-given-name" name="given-name" type="text" autocomplete="given-name">
5
+ </div>
6
+ <div class="ef-field">
7
+ <label class="ef-field__label" for="form-row-family-name">Family name</label>
8
+ <span class="ef-field__description" id="form-row-family-name-description">As printed on your passport or national identity card.</span>
9
+ <input id="form-row-family-name" name="family-name" type="text" autocomplete="family-name" aria-describedby="form-row-family-name-description">
10
+ </div>
11
+ <div class="ef-field">
12
+ <label class="ef-field__label" for="form-row-birth-date">Date of birth</label>
13
+ <input id="form-row-birth-date" name="birth-date" type="date" autocomplete="bday">
14
+ </div>
15
+ </div>
@@ -670,6 +670,16 @@
670
670
  "status": "requires-published-figma-component"
671
671
  }
672
672
  },
673
+ {
674
+ "id": "form-row",
675
+ "figmaName": "Forma / Form Row",
676
+ "source": "patterns/form-row.html",
677
+ "codeConnect": {
678
+ "id": "Forma/form-row",
679
+ "nodeUrl": null,
680
+ "status": "requires-published-figma-component"
681
+ }
682
+ },
673
683
  {
674
684
  "id": "grid",
675
685
  "figmaName": "Forma / Grid",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@echelon-foundry/design-system",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Zero-runtime HTML and CSS design system for Echelon Foundry applications.",
@@ -64,11 +64,11 @@
64
64
  "test:motion": "node --test tests/motion-audit.test.mjs tests/motion-gate.test.mjs && node tools/motion-audit.mjs --strict",
65
65
  "check": "npm run build && npm run icons:check && npm run test:tokens && npm run test:brands && npm run test:runtime && npm run test:motion && npm run test:marketing && npm run test:character-grid && npm run test:figma && npm run test:css && npm run test:package && npm run workflow:test && npm run workflow:build && npm run test:workflow-package && npm run test:browser",
66
66
  "site:build": "npm run build && node tools/build-site.mjs",
67
- "site:test": "node --test tests/site-build.test.mjs tests/catalog-coverage.test.mjs tests/icon-docs.test.mjs",
67
+ "site:test": "node --test tests/site-build.test.mjs tests/catalog-coverage.test.mjs tests/icon-docs.test.mjs tests/catalog-interaction.test.mjs",
68
68
  "site:check": "npm run site:build && npm run catalog:check && npm run site:test && npm run site:mobile",
69
69
  "site:mobile": "playwright test --config=playwright.site.config.mjs",
70
70
  "pack:verify": "npm run build && npm pack --dry-run",
71
- "test:package": "node --test tests/package-contract.test.mjs",
71
+ "test:package": "node --test tests/package-contract.test.mjs tests/conformance-package.test.mjs",
72
72
  "test:css": "node --test tests/css-source.test.mjs tests/dist-css.test.mjs tests/contrast-pairs.test.mjs tests/diagram-contract.test.mjs",
73
73
  "test:contrast": "node --test tests/contrast-pairs.test.mjs && playwright test tests/browser/pattern-contrast.spec.mjs --project=chromium",
74
74
  "brands:build": "dotnet run --project tools/BrandCompiler/BrandCompiler.fsproj -- brands dist/brands",
@@ -90,6 +90,7 @@
90
90
  "test:workflow-browser": "playwright test tests/browser/workflow-static.spec.mjs tests/browser/workflow-embed.spec.mjs",
91
91
  "workflow:check": "npm run workflow:test && npm run workflow:build && npm run test:workflow-package && npm run test:workflow-browser",
92
92
  "test:machine": "playwright test tests/browser/machine-operability.spec.mjs",
93
+ "ui:conformance": "npm run site:build && npm run catalog:check && npm run test:package && playwright test --config=playwright.conformance.config.mjs",
93
94
  "icons:check": "node --test tests/icons.test.mjs"
94
95
  },
95
96
  "devDependencies": {
@@ -0,0 +1,15 @@
1
+ <div class="ef-form-row">
2
+ <div class="ef-field">
3
+ <label class="ef-field__label" for="form-row-given-name">Given name</label>
4
+ <input id="form-row-given-name" name="given-name" type="text" autocomplete="given-name">
5
+ </div>
6
+ <div class="ef-field">
7
+ <label class="ef-field__label" for="form-row-family-name">Family name</label>
8
+ <span class="ef-field__description" id="form-row-family-name-description">As printed on your passport or national identity card.</span>
9
+ <input id="form-row-family-name" name="family-name" type="text" autocomplete="family-name" aria-describedby="form-row-family-name-description">
10
+ </div>
11
+ <div class="ef-field">
12
+ <label class="ef-field__label" for="form-row-birth-date">Date of birth</label>
13
+ <input id="form-row-birth-date" name="birth-date" type="date" autocomplete="bday">
14
+ </div>
15
+ </div>