@echelon-foundry/design-system 0.7.0 → 0.8.0-main.249.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 CHANGED
@@ -7,6 +7,22 @@ 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.8.0
11
+
12
+ Minor release: Design by Construction forms and fully operable form examples.
13
+
14
+ ### Added
15
+ - A versioned `forma.design/1.0.0` JSON contract, capability declaration, validator, deterministic HTML renderer, valid NASA fixture and refusal fixtures for unsupported versions, presentation overrides, invalid nesting, misalignment and unbound actions.
16
+ - A complete form catalog page with functional basic, advanced, validation and mobile examples built from native custom-element HTML.
17
+ - Package exports for the design schema, capabilities and examples, while preserving hand-authored HTML and audited extended/custom escape paths.
18
+
19
+ ### Fixed
20
+ - Forms now have a bounded responsive root, aligned grid-field tracks, stable validation-message placement and visible fragment-targeted success feedback.
21
+ - Native controls and buttons have explicit disabled, read-only and invalid presentation without relying on colour alone.
22
+ - Catalog reset examples are outcome-tested by preparing and observing a native field value; submit examples remain URL-outcome tested.
23
+
24
+ No existing component, class, token or icon was renamed or removed. Forma remains zero-runtime HTML and CSS; design validation and rendering are build-time tools.
25
+
10
26
  ## 0.7.0
11
27
 
12
28
  Minor release: shared layout and interaction conformance with
package/LICENSE CHANGED
@@ -1,21 +1,131 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Kevin Miller
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ # PolyForm Noncommercial License 1.0.0
2
+
3
+ <https://polyformproject.org/licenses/noncommercial/1.0.0>
4
+
5
+ ## Acceptance
6
+
7
+ In order to get any license under these terms, you must agree
8
+ to them as both strict obligations and conditions to all
9
+ your licenses.
10
+
11
+ ## Copyright License
12
+
13
+ The licensor grants you a copyright license for the
14
+ software to do everything you might do with the software
15
+ that would otherwise infringe the licensor's copyright
16
+ in it for any permitted purpose. However, you may
17
+ only distribute the software according to [Distribution
18
+ License](#distribution-license) and make changes or new works
19
+ based on the software according to [Changes and New Works
20
+ License](#changes-and-new-works-license).
21
+
22
+ ## Distribution License
23
+
24
+ The licensor grants you an additional copyright license
25
+ to distribute copies of the software. Your license
26
+ to distribute covers distributing the software with
27
+ changes and new works permitted by [Changes and New Works
28
+ License](#changes-and-new-works-license).
29
+
30
+ ## Notices
31
+
32
+ You must ensure that anyone who gets a copy of any part of
33
+ the software from you also gets a copy of these terms or the
34
+ URL for them above, as well as copies of any plain-text lines
35
+ beginning with `Required Notice:` that the licensor provided
36
+ with the software. For example:
37
+
38
+ > Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
39
+
40
+ ## Changes and New Works License
41
+
42
+ The licensor grants you an additional copyright license to
43
+ make changes and new works based on the software for any
44
+ permitted purpose.
45
+
46
+ ## Patent License
47
+
48
+ The licensor grants you a patent license for the software that
49
+ covers patent claims the licensor can license, or becomes able
50
+ to license, that you would infringe by using the software.
51
+
52
+ ## Noncommercial Purposes
53
+
54
+ Any noncommercial purpose is a permitted purpose.
55
+
56
+ ## Personal Uses
57
+
58
+ Personal use for research, experiment, and testing for
59
+ the benefit of public knowledge, personal study, private
60
+ entertainment, hobby projects, amateur pursuits, or religious
61
+ observance, without any anticipated commercial application,
62
+ is use for a permitted purpose.
63
+
64
+ ## Noncommercial Organizations
65
+
66
+ Use by any charitable organization, educational institution,
67
+ public research organization, public safety or health
68
+ organization, environmental protection organization,
69
+ or government institution is use for a permitted purpose
70
+ regardless of the source of funding or obligations resulting
71
+ from the funding.
72
+
73
+ ## Fair Use
74
+
75
+ You may have "fair use" rights for the software under the
76
+ law. These terms do not limit them.
77
+
78
+ ## No Other Rights
79
+
80
+ These terms do not allow you to sublicense or transfer any of
81
+ your licenses to anyone else, or prevent the licensor from
82
+ granting licenses to anyone else. These terms do not imply
83
+ any other licenses.
84
+
85
+ ## Patent Defense
86
+
87
+ If you make any written claim that the software infringes or
88
+ contributes to infringement of any patent, your patent license
89
+ for the software granted under these terms ends immediately. If
90
+ your company makes such a claim, your patent license ends
91
+ immediately for work on behalf of your company.
92
+
93
+ ## Violations
94
+
95
+ The first time you are notified in writing that you have
96
+ violated any of these terms, or done anything with the software
97
+ not covered by your licenses, your licenses can nonetheless
98
+ continue if you come into full compliance with these terms,
99
+ and take practical steps to correct past violations, within
100
+ 32 days of receiving notice. Otherwise, all your licenses
101
+ end immediately.
102
+
103
+ ## No Liability
104
+
105
+ ***As far as the law allows, the software comes as is, without
106
+ any warranty or condition, and the licensor will not be liable
107
+ to you for any damages arising out of these terms or the use
108
+ or nature of the software, under any kind of legal claim.***
109
+
110
+ ## Definitions
111
+
112
+ The **licensor** is the individual or entity offering these
113
+ terms, and the **software** is the software the licensor makes
114
+ available under these terms.
115
+
116
+ **You** refers to the individual or entity agreeing to these
117
+ terms.
118
+
119
+ **Your company** is any legal entity, sole proprietorship,
120
+ or other kind of organization that you work for, plus all
121
+ organizations that have control over, are under the control of,
122
+ or are under common control with that organization. **Control**
123
+ means ownership of substantially all the assets of an entity,
124
+ or the power to direct its management and policies by vote,
125
+ contract, or otherwise. Control can be direct or indirect.
126
+
127
+ **Your licenses** are all the licenses granted to you for the
128
+ software under these terms.
129
+
130
+ **Use** means anything you do with the software requiring one
131
+ of your licenses.
package/README.md CHANGED
@@ -155,3 +155,8 @@ See [docs/FIGMA.md](docs/FIGMA.md) for the variable, component, and Code Connect
155
155
  workflow. The public package also exposes `./tokens.json` and
156
156
  `./figma/components.json` for design tooling.
157
157
 
158
+ ## License
159
+
160
+ Source available under PolyForm Noncommercial 1.0.0 for permitted noncommercial uses. Commercial use requires a separate license from Echelon Foundry. Previously distributed versions remain subject to the licenses under which they were released.
161
+
162
+ Commercial licensing information is forthcoming.
@@ -0,0 +1,13 @@
1
+ {
2
+ "format": "forma.design-capabilities",
3
+ "schemaVersion": "1.0.0",
4
+ "contractVersions": ["1.0.0"],
5
+ "modes": ["standard", "extended", "custom"],
6
+ "formLayouts": ["stack", "grid"],
7
+ "catalogLayoutModels": ["stack", "grid", "split", "dock", "flow", "canvas", "overlay", "page", "repeater"],
8
+ "controls": ["text", "email", "tel", "url", "number", "date", "time", "datetime-local", "textarea", "select", "checkbox", "radio-group", "toggle"],
9
+ "standardActionBindings": ["form.submit", "form.reset"],
10
+ "minimumViewportCssPx": 320,
11
+ "standardOverflow": "wrap",
12
+ "runtime": "none"
13
+ }
package/dist/all.css CHANGED
@@ -631,6 +631,12 @@
631
631
  transition-duration: var(--ef-motion-direct-duration, 0ms);
632
632
  }
633
633
 
634
+ :where(button, .ef-button):disabled {
635
+ border-style: dashed;
636
+ cursor: not-allowed;
637
+ opacity: 0.62;
638
+ }
639
+
634
640
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
635
641
  box-sizing: border-box;
636
642
  min-block-size: 2.75rem;
@@ -686,6 +692,28 @@
686
692
  text-align: start;
687
693
  }
688
694
 
695
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):disabled {
696
+ --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c);
697
+ --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
698
+ --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
699
+ border-style: dashed;
700
+ background: var(--ef-color-surface-secondary, #e3e0d7);
701
+ cursor: not-allowed;
702
+ opacity: 0.62;
703
+ }
704
+
705
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea):read-only:not(:disabled) {
706
+ border-style: dashed;
707
+ background: color-mix(in srgb, var(--ef-color-surface-secondary, #e3e0d7) 55%, transparent);
708
+ }
709
+
710
+ /* Invalid state keeps native semantics and adds a shape cue. The associated
711
+ recovery message remains the non-colour explanation of what to fix. */
712
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):where([aria-invalid="true"], :user-invalid) {
713
+ border-inline-start-width: 0.3rem;
714
+ border-inline-start-color: var(--ef-color-status-danger, #8d3027);
715
+ }
716
+
689
717
  :where(:focus-visible) {
690
718
  outline: 3px solid var(--ef-color-focus-gap, #f2efe7);
691
719
  outline-offset: 2px;
@@ -870,6 +898,39 @@
870
898
  .ef-form-row > .ef-field > :not(.ef-field__label, .ef-field__description, .ef-validation-message) { grid-row: 3; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
871
899
  .ef-form-row > .ef-field > .ef-validation-message { grid-row: 4; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
872
900
 
901
+ /* Form contract ------------------------------------------------------- */
902
+ .ef-form {
903
+ container-type: inline-size;
904
+ display: grid;
905
+ gap: var(--ef-form-gap, var(--ef-primitive-spacing-5, 1.5rem));
906
+ min-inline-size: 0;
907
+ max-inline-size: var(--ef-form-max, 64rem);
908
+ }
909
+
910
+ .ef-form > :where(h1, h2, h3, p) { margin-block: 0; }
911
+
912
+ /* Native constraint validation owns validity. The recovery text is in the
913
+ DOM and programmatically associated from first render, but appears only
914
+ after the browser marks the control user-invalid. */
915
+ .ef-validation-message--when-invalid { display: none; }
916
+ .ef-field:has(> :user-invalid) > .ef-validation-message--when-invalid { display: flex; }
917
+
918
+ /* GET submission to the declared fragment is a real, script-free outcome.
919
+ The status remains absent from layout and the accessibility tree until it
920
+ is the URL target. */
921
+ .ef-form__outcome { display: none; }
922
+ .ef-form__outcome:target {
923
+ --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c);
924
+ --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
925
+ --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
926
+ display: block;
927
+ padding: var(--ef-primitive-spacing-3, 0.75rem);
928
+ border-inline-start: 0.25rem solid var(--ef-color-status-success, #2f6f4e);
929
+ background: var(--ef-color-surface-secondary, #e3e0d7);
930
+ color: var(--ef-color-text-on-secondary-surface, #3a403c);
931
+ font-weight: 650;
932
+ }
933
+
873
934
  .ef-field > :is(input[type="date"], input[type="time"], input[type="datetime-local"]) {
874
935
  min-inline-size: 0;
875
936
  inline-size: 100%;
@@ -6235,7 +6296,6 @@
6235
6296
  }
6236
6297
  }
6237
6298
 
6238
-
6239
6299
  @layer echelon.assessment {
6240
6300
  /* Question shell ------------------------------------------------------ */
6241
6301
  .ef-question {
@@ -232,6 +232,7 @@
232
232
  "patterns/flyout.html",
233
233
  "patterns/focus-stage.html",
234
234
  "patterns/form-row.html",
235
+ "patterns/form.html",
235
236
  "patterns/frame.html",
236
237
  "patterns/freshness.html",
237
238
  "patterns/grid.html",
@@ -340,13 +341,13 @@
340
341
  "tokens.css"
341
342
  ],
342
343
  "bytes": {
343
- "all.css": 326931,
344
+ "all.css": 329656,
344
345
  "assessment.css": 36343,
345
346
  "brands/echelon.css": 3621,
346
347
  "brands/example-harbor.css": 3637,
347
348
  "brands/index.json": 335,
348
- "components.css": 209407,
349
- "foundations.css": 14098,
349
+ "components.css": 210904,
350
+ "foundations.css": 15326,
350
351
  "icons/add.svg": 216,
351
352
  "icons/agent.svg": 354,
352
353
  "icons/archive.svg": 248,
@@ -572,6 +573,7 @@
572
573
  "patterns/flyout.html": 2488,
573
574
  "patterns/focus-stage.html": 377,
574
575
  "patterns/form-row.html": 848,
576
+ "patterns/form.html": 2000,
575
577
  "patterns/frame.html": 365,
576
578
  "patterns/freshness.html": 187,
577
579
  "patterns/grid.html": 305,
@@ -64,6 +64,39 @@
64
64
  .ef-form-row > .ef-field > :not(.ef-field__label, .ef-field__description, .ef-validation-message) { grid-row: 3; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
65
65
  .ef-form-row > .ef-field > .ef-validation-message { grid-row: 4; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
66
66
 
67
+ /* Form contract ------------------------------------------------------- */
68
+ .ef-form {
69
+ container-type: inline-size;
70
+ display: grid;
71
+ gap: var(--ef-form-gap, var(--ef-primitive-spacing-5, 1.5rem));
72
+ min-inline-size: 0;
73
+ max-inline-size: var(--ef-form-max, 64rem);
74
+ }
75
+
76
+ .ef-form > :where(h1, h2, h3, p) { margin-block: 0; }
77
+
78
+ /* Native constraint validation owns validity. The recovery text is in the
79
+ DOM and programmatically associated from first render, but appears only
80
+ after the browser marks the control user-invalid. */
81
+ .ef-validation-message--when-invalid { display: none; }
82
+ .ef-field:has(> :user-invalid) > .ef-validation-message--when-invalid { display: flex; }
83
+
84
+ /* GET submission to the declared fragment is a real, script-free outcome.
85
+ The status remains absent from layout and the accessibility tree until it
86
+ is the URL target. */
87
+ .ef-form__outcome { display: none; }
88
+ .ef-form__outcome:target {
89
+ --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c);
90
+ --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
91
+ --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
92
+ display: block;
93
+ padding: var(--ef-primitive-spacing-3, 0.75rem);
94
+ border-inline-start: 0.25rem solid var(--ef-color-status-success, #2f6f4e);
95
+ background: var(--ef-color-surface-secondary, #e3e0d7);
96
+ color: var(--ef-color-text-on-secondary-surface, #3a403c);
97
+ font-weight: 650;
98
+ }
99
+
67
100
  .ef-field > :is(input[type="date"], input[type="time"], input[type="datetime-local"]) {
68
101
  min-inline-size: 0;
69
102
  inline-size: 100%;
@@ -5428,4 +5461,3 @@
5428
5461
  .ef-icon { color: CanvasText; }
5429
5462
  }
5430
5463
  }
5431
-
@@ -96,6 +96,12 @@
96
96
  transition-duration: var(--ef-motion-direct-duration, 0ms);
97
97
  }
98
98
 
99
+ :where(button, .ef-button):disabled {
100
+ border-style: dashed;
101
+ cursor: not-allowed;
102
+ opacity: 0.62;
103
+ }
104
+
99
105
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
100
106
  box-sizing: border-box;
101
107
  min-block-size: 2.75rem;
@@ -151,6 +157,28 @@
151
157
  text-align: start;
152
158
  }
153
159
 
160
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):disabled {
161
+ --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c);
162
+ --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
163
+ --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
164
+ border-style: dashed;
165
+ background: var(--ef-color-surface-secondary, #e3e0d7);
166
+ cursor: not-allowed;
167
+ opacity: 0.62;
168
+ }
169
+
170
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea):read-only:not(:disabled) {
171
+ border-style: dashed;
172
+ background: color-mix(in srgb, var(--ef-color-surface-secondary, #e3e0d7) 55%, transparent);
173
+ }
174
+
175
+ /* Invalid state keeps native semantics and adds a shape cue. The associated
176
+ recovery message remains the non-colour explanation of what to fix. */
177
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):where([aria-invalid="true"], :user-invalid) {
178
+ border-inline-start-width: 0.3rem;
179
+ border-inline-start-color: var(--ef-color-status-danger, #8d3027);
180
+ }
181
+
154
182
  :where(:focus-visible) {
155
183
  outline: 3px solid var(--ef-color-focus-gap, #f2efe7);
156
184
  outline-offset: 2px;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "formaVersion": "0.7.0",
3
+ "formaVersion": "0.8.0",
4
4
  "grid": 24,
5
5
  "icons": [
6
6
  {
@@ -1,4 +1,4 @@
1
- /*! Forma 0.7.0 | forma-all.css | bundle echelon-marketing
1
+ /*! Forma 0.8.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
 
@@ -636,6 +636,12 @@
636
636
  transition-duration: var(--ef-motion-direct-duration, 0ms);
637
637
  }
638
638
 
639
+ :where(button, .ef-button):disabled {
640
+ border-style: dashed;
641
+ cursor: not-allowed;
642
+ opacity: 0.62;
643
+ }
644
+
639
645
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
640
646
  box-sizing: border-box;
641
647
  min-block-size: 2.75rem;
@@ -691,6 +697,28 @@
691
697
  text-align: start;
692
698
  }
693
699
 
700
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):disabled {
701
+ --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c);
702
+ --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
703
+ --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
704
+ border-style: dashed;
705
+ background: var(--ef-color-surface-secondary, #e3e0d7);
706
+ cursor: not-allowed;
707
+ opacity: 0.62;
708
+ }
709
+
710
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea):read-only:not(:disabled) {
711
+ border-style: dashed;
712
+ background: color-mix(in srgb, var(--ef-color-surface-secondary, #e3e0d7) 55%, transparent);
713
+ }
714
+
715
+ /* Invalid state keeps native semantics and adds a shape cue. The associated
716
+ recovery message remains the non-colour explanation of what to fix. */
717
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):where([aria-invalid="true"], :user-invalid) {
718
+ border-inline-start-width: 0.3rem;
719
+ border-inline-start-color: var(--ef-color-status-danger, #8d3027);
720
+ }
721
+
694
722
  :where(:focus-visible) {
695
723
  outline: 3px solid var(--ef-color-focus-gap, #f2efe7);
696
724
  outline-offset: 2px;
@@ -875,6 +903,39 @@
875
903
  .ef-form-row > .ef-field > :not(.ef-field__label, .ef-field__description, .ef-validation-message) { grid-row: 3; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
876
904
  .ef-form-row > .ef-field > .ef-validation-message { grid-row: 4; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
877
905
 
906
+ /* Form contract ------------------------------------------------------- */
907
+ .ef-form {
908
+ container-type: inline-size;
909
+ display: grid;
910
+ gap: var(--ef-form-gap, var(--ef-primitive-spacing-5, 1.5rem));
911
+ min-inline-size: 0;
912
+ max-inline-size: var(--ef-form-max, 64rem);
913
+ }
914
+
915
+ .ef-form > :where(h1, h2, h3, p) { margin-block: 0; }
916
+
917
+ /* Native constraint validation owns validity. The recovery text is in the
918
+ DOM and programmatically associated from first render, but appears only
919
+ after the browser marks the control user-invalid. */
920
+ .ef-validation-message--when-invalid { display: none; }
921
+ .ef-field:has(> :user-invalid) > .ef-validation-message--when-invalid { display: flex; }
922
+
923
+ /* GET submission to the declared fragment is a real, script-free outcome.
924
+ The status remains absent from layout and the accessibility tree until it
925
+ is the URL target. */
926
+ .ef-form__outcome { display: none; }
927
+ .ef-form__outcome:target {
928
+ --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c);
929
+ --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
930
+ --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
931
+ display: block;
932
+ padding: var(--ef-primitive-spacing-3, 0.75rem);
933
+ border-inline-start: 0.25rem solid var(--ef-color-status-success, #2f6f4e);
934
+ background: var(--ef-color-surface-secondary, #e3e0d7);
935
+ color: var(--ef-color-text-on-secondary-surface, #3a403c);
936
+ font-weight: 650;
937
+ }
938
+
878
939
  .ef-field > :is(input[type="date"], input[type="time"], input[type="datetime-local"]) {
879
940
  min-inline-size: 0;
880
941
  inline-size: 100%;
@@ -6240,7 +6301,6 @@
6240
6301
  }
6241
6302
  }
6242
6303
 
6243
-
6244
6304
  @layer echelon.assessment {
6245
6305
  /* Question shell ------------------------------------------------------ */
6246
6306
  .ef-question {
@@ -1,4 +1,4 @@
1
- /*! Forma 0.7.0 | forma-echelon-marketing.css | bundle echelon-marketing
1
+ /*! Forma 0.8.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
 
@@ -736,6 +736,12 @@
736
736
  transition-duration: var(--ef-motion-direct-duration, 0ms);
737
737
  }
738
738
 
739
+ :where(button, .ef-button):disabled {
740
+ border-style: dashed;
741
+ cursor: not-allowed;
742
+ opacity: 0.62;
743
+ }
744
+
739
745
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
740
746
  box-sizing: border-box;
741
747
  min-block-size: 2.75rem;
@@ -791,6 +797,28 @@
791
797
  text-align: start;
792
798
  }
793
799
 
800
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):disabled {
801
+ --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c);
802
+ --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
803
+ --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
804
+ border-style: dashed;
805
+ background: var(--ef-color-surface-secondary, #e3e0d7);
806
+ cursor: not-allowed;
807
+ opacity: 0.62;
808
+ }
809
+
810
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea):read-only:not(:disabled) {
811
+ border-style: dashed;
812
+ background: color-mix(in srgb, var(--ef-color-surface-secondary, #e3e0d7) 55%, transparent);
813
+ }
814
+
815
+ /* Invalid state keeps native semantics and adds a shape cue. The associated
816
+ recovery message remains the non-colour explanation of what to fix. */
817
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):where([aria-invalid="true"], :user-invalid) {
818
+ border-inline-start-width: 0.3rem;
819
+ border-inline-start-color: var(--ef-color-status-danger, #8d3027);
820
+ }
821
+
794
822
  :where(:focus-visible) {
795
823
  outline: 3px solid var(--ef-color-focus-gap, #f2efe7);
796
824
  outline-offset: 2px;
@@ -1,4 +1,4 @@
1
- /*! Forma 0.7.0 | forma-marketing-theme-echelon.css | bundle echelon-marketing
1
+ /*! Forma 0.8.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.7.0 | forma-marketing.css | bundle echelon-marketing
1
+ /*! Forma 0.8.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
 
@@ -735,6 +735,12 @@
735
735
  transition-duration: var(--ef-motion-direct-duration, 0ms);
736
736
  }
737
737
 
738
+ :where(button, .ef-button):disabled {
739
+ border-style: dashed;
740
+ cursor: not-allowed;
741
+ opacity: 0.62;
742
+ }
743
+
738
744
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
739
745
  box-sizing: border-box;
740
746
  min-block-size: 2.75rem;
@@ -790,6 +796,28 @@
790
796
  text-align: start;
791
797
  }
792
798
 
799
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):disabled {
800
+ --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c);
801
+ --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
802
+ --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
803
+ border-style: dashed;
804
+ background: var(--ef-color-surface-secondary, #e3e0d7);
805
+ cursor: not-allowed;
806
+ opacity: 0.62;
807
+ }
808
+
809
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea):read-only:not(:disabled) {
810
+ border-style: dashed;
811
+ background: color-mix(in srgb, var(--ef-color-surface-secondary, #e3e0d7) 55%, transparent);
812
+ }
813
+
814
+ /* Invalid state keeps native semantics and adds a shape cue. The associated
815
+ recovery message remains the non-colour explanation of what to fix. */
816
+ :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):where([aria-invalid="true"], :user-invalid) {
817
+ border-inline-start-width: 0.3rem;
818
+ border-inline-start-color: var(--ef-color-status-danger, #8d3027);
819
+ }
820
+
793
821
  :where(:focus-visible) {
794
822
  outline: 3px solid var(--ef-color-focus-gap, #f2efe7);
795
823
  outline-offset: 2px;
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "forma": "0.7.0",
3
+ "forma": "0.8.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.7.0",
6
+ "releaseTag": "v0.8.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": 81740,
12
- "sha256": "171d3cb1485da7a9f83f6bdfc80c8043b0d194a32eadc3e0b5e09b12fade2f80",
11
+ "bytes": 82968,
12
+ "sha256": "3089398445d5898cdae9cc0dc2ec4629e8130a1a885291d9ae1bfdf9141dab48",
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": "027334d7946a3342812dca297967630cb5f5b1e96dd849672c8e146bf257b153",
26
+ "sha256": "970b7986b609331c1bc12d51173c8e185f48906df5d8b2fcc585c3187be3e9fd",
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": 87880,
36
- "sha256": "2baacb6423cc930e15b1da5cbf39289bed5bd3c721e35ef2e97cb73f48c0c0fc",
35
+ "bytes": 89108,
36
+ "sha256": "ee17288af3ae5ba708b8fd1b471835a14ea93da588a39206dcc27db2ee2f98ba",
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": 327293,
46
- "sha256": "5aae64c6fa2e8784347b2207fe7324dae2492a5f45f6b134463e243f419b4dec",
45
+ "bytes": 330018,
46
+ "sha256": "93cafe35f219e7297b8ad5cd8b3a22ad660e753a2ce247c0709eb75281701cad",
47
47
  "sources": [
48
48
  "dist/all.css"
49
49
  ]