@echelon-foundry/design-system 0.2.0-main.15.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/README.md +82 -0
- package/dist/all.css +3461 -0
- package/dist/assessment.css +1123 -0
- package/dist/brands/echelon.css +91 -0
- package/dist/brands/example-harbor.css +91 -0
- package/dist/brands/index.json +17 -0
- package/dist/build-metrics.json +152 -0
- package/dist/components.css +2123 -0
- package/dist/foundations.css +113 -0
- package/dist/patterns/alert.html +8 -0
- package/dist/patterns/allocation.html +24 -0
- package/dist/patterns/best-worst.html +21 -0
- package/dist/patterns/binary-choice.html +15 -0
- package/dist/patterns/checkbox.html +13 -0
- package/dist/patterns/choice-group.html +27 -0
- package/dist/patterns/collection-toolbar.html +22 -0
- package/dist/patterns/combobox.html +13 -0
- package/dist/patterns/command-palette.html +14 -0
- package/dist/patterns/composer.html +19 -0
- package/dist/patterns/conflict-review.html +17 -0
- package/dist/patterns/conversation.html +23 -0
- package/dist/patterns/dashboard-grid.html +5 -0
- package/dist/patterns/data-grid.html +30 -0
- package/dist/patterns/date-range.html +13 -0
- package/dist/patterns/dialog.html +15 -0
- package/dist/patterns/diff-viewer.html +22 -0
- package/dist/patterns/disclosure.html +6 -0
- package/dist/patterns/empty-state.html +6 -0
- package/dist/patterns/file-upload.html +19 -0
- package/dist/patterns/flyout.html +72 -0
- package/dist/patterns/hierarchical-choice.html +25 -0
- package/dist/patterns/hierarchical-multi-choice.html +27 -0
- package/dist/patterns/image-choice.html +25 -0
- package/dist/patterns/master-detail.html +16 -0
- package/dist/patterns/matrix-single.html +26 -0
- package/dist/patterns/menu.html +8 -0
- package/dist/patterns/metric-card.html +6 -0
- package/dist/patterns/mobile-action-bar.html +4 -0
- package/dist/patterns/multi-choice.html +23 -0
- package/dist/patterns/numeric-stepper.html +5 -0
- package/dist/patterns/obligation-panel.html +36 -0
- package/dist/patterns/operation-status.html +12 -0
- package/dist/patterns/ordinal-scale.html +42 -0
- package/dist/patterns/pagination.html +12 -0
- package/dist/patterns/pairwise-choice.html +21 -0
- package/dist/patterns/popover.html +6 -0
- package/dist/patterns/preview-surface.html +14 -0
- package/dist/patterns/provenance-trail.html +9 -0
- package/dist/patterns/question.html +13 -0
- package/dist/patterns/range-entry.html +18 -0
- package/dist/patterns/ranking.html +32 -0
- package/dist/patterns/readiness-checklist.html +12 -0
- package/dist/patterns/record-header.html +17 -0
- package/dist/patterns/rule-builder.html +37 -0
- package/dist/patterns/search.html +8 -0
- package/dist/patterns/segmented-control.html +13 -0
- package/dist/patterns/select.html +12 -0
- package/dist/patterns/semantic-differential.html +23 -0
- package/dist/patterns/skeleton.html +7 -0
- package/dist/patterns/slider.html +11 -0
- package/dist/patterns/special-choice.html +15 -0
- package/dist/patterns/status-lozenge.html +6 -0
- package/dist/patterns/survey-progress.html +8 -0
- package/dist/patterns/switch.html +14 -0
- package/dist/patterns/symbol-rating.html +31 -0
- package/dist/patterns/tabs.html +11 -0
- package/dist/patterns/timeline.html +19 -0
- package/dist/patterns/toast.html +10 -0
- package/dist/patterns/understanding.html +32 -0
- package/dist/patterns/validation-message.html +4 -0
- package/dist/patterns/validation-summary.html +10 -0
- package/dist/patterns/wizard.html +19 -0
- package/dist/patterns/work-queue.html +20 -0
- package/dist/skins.css +29 -0
- package/dist/tokens.css +99 -0
- package/docs/BRANDING.md +148 -0
- package/package.json +60 -0
- package/patterns/alert.html +8 -0
- package/patterns/allocation.html +24 -0
- package/patterns/assessment.manifest.md +93 -0
- package/patterns/best-worst.html +21 -0
- package/patterns/binary-choice.html +15 -0
- package/patterns/checkbox.html +13 -0
- package/patterns/choice-group.html +27 -0
- package/patterns/collection-toolbar.html +22 -0
- package/patterns/combobox.html +13 -0
- package/patterns/command-palette.html +14 -0
- package/patterns/composer.html +19 -0
- package/patterns/composer.manifest.md +27 -0
- package/patterns/conflict-review.html +17 -0
- package/patterns/conversation.html +23 -0
- package/patterns/conversation.manifest.md +21 -0
- package/patterns/dashboard-grid.html +5 -0
- package/patterns/data-grid.html +30 -0
- package/patterns/date-range.html +13 -0
- package/patterns/dialog.html +15 -0
- package/patterns/diff-viewer.html +22 -0
- package/patterns/disclosure.html +6 -0
- package/patterns/empty-state.html +6 -0
- package/patterns/file-upload.html +19 -0
- package/patterns/flyout.html +72 -0
- package/patterns/flyout.manifest.md +43 -0
- package/patterns/hierarchical-choice.html +25 -0
- package/patterns/hierarchical-multi-choice.html +27 -0
- package/patterns/image-choice.html +25 -0
- package/patterns/manifest.md +46 -0
- package/patterns/master-detail.html +16 -0
- package/patterns/matrix-single.html +26 -0
- package/patterns/menu.html +8 -0
- package/patterns/metric-card.html +6 -0
- package/patterns/mobile-action-bar.html +4 -0
- package/patterns/multi-choice.html +23 -0
- package/patterns/numeric-stepper.html +5 -0
- package/patterns/obligation-panel.html +36 -0
- package/patterns/operation-status.html +12 -0
- package/patterns/ordinal-scale.html +42 -0
- package/patterns/pagination.html +12 -0
- package/patterns/pairwise-choice.html +21 -0
- package/patterns/popover.html +6 -0
- package/patterns/preview-surface.html +14 -0
- package/patterns/provenance-trail.html +9 -0
- package/patterns/question.html +13 -0
- package/patterns/range-entry.html +18 -0
- package/patterns/ranking.html +32 -0
- package/patterns/readiness-checklist.html +12 -0
- package/patterns/record-header.html +17 -0
- package/patterns/rule-builder.html +37 -0
- package/patterns/search.html +8 -0
- package/patterns/segmented-control.html +13 -0
- package/patterns/select.html +12 -0
- package/patterns/semantic-differential.html +23 -0
- package/patterns/skeleton.html +7 -0
- package/patterns/slider.html +11 -0
- package/patterns/special-choice.html +15 -0
- package/patterns/status-lozenge.html +6 -0
- package/patterns/survey-progress.html +8 -0
- package/patterns/switch.html +14 -0
- package/patterns/symbol-rating.html +31 -0
- package/patterns/tabs.html +11 -0
- package/patterns/timeline.html +19 -0
- package/patterns/toast.html +10 -0
- package/patterns/understanding.html +32 -0
- package/patterns/understanding.manifest.md +26 -0
- package/patterns/validation-message.html +4 -0
- package/patterns/validation-summary.html +10 -0
- package/patterns/wizard.html +19 -0
- package/patterns/work-queue.html +20 -0
- package/schemas/brand-manifest.schema.json +152 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<button type="button" popovertarget="save-notification" popovertargetaction="show">Show notification</button>
|
|
2
|
+
<aside class="ef-toast" id="save-notification" popover="manual" role="status" data-ef-motion-weight="standard">
|
|
3
|
+
<div class="ef-toast__header">
|
|
4
|
+
<div>
|
|
5
|
+
<h3 class="ef-toast__title">Changes saved</h3>
|
|
6
|
+
<p>The current draft is stored.</p>
|
|
7
|
+
</div>
|
|
8
|
+
<button type="button" popovertarget="save-notification" popovertargetaction="hide" aria-label="Dismiss notification">Close</button>
|
|
9
|
+
</div>
|
|
10
|
+
</aside>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<section class="ef-understanding" aria-labelledby="understanding-title">
|
|
2
|
+
<header class="ef-understanding__header">
|
|
3
|
+
<div>
|
|
4
|
+
<span class="ef-component-kicker">Understanding</span>
|
|
5
|
+
<h2 id="understanding-title">I found 4 things</h2>
|
|
6
|
+
<p>Review what the application understood before any consequential transition.</p>
|
|
7
|
+
</div>
|
|
8
|
+
<span class="ef-status-lozenge" data-state="attention">1 question</span>
|
|
9
|
+
</header>
|
|
10
|
+
<ol class="ef-understanding__items">
|
|
11
|
+
<li class="ef-understanding__item" data-ef-state="accepted">
|
|
12
|
+
<span class="ef-understanding__state">Understood</span>
|
|
13
|
+
<div><strong>Medication change</strong><p>Medication increased to 40 mg.</p></div>
|
|
14
|
+
<button type="button">Source</button>
|
|
15
|
+
</li>
|
|
16
|
+
<li class="ef-understanding__item" data-ef-state="accepted">
|
|
17
|
+
<span class="ef-understanding__state">Understood</span>
|
|
18
|
+
<div><strong>Symptom</strong><p>Ankle swelling returned.</p></div>
|
|
19
|
+
<button type="button">Source</button>
|
|
20
|
+
</li>
|
|
21
|
+
<li class="ef-understanding__item" data-ef-state="unknown">
|
|
22
|
+
<span class="ef-understanding__state">Unknown</span>
|
|
23
|
+
<div><strong>Medication frequency</strong><p>The dose frequency was not supplied.</p></div>
|
|
24
|
+
<button type="button">Answer</button>
|
|
25
|
+
</li>
|
|
26
|
+
<li class="ef-understanding__item" data-ef-state="accepted">
|
|
27
|
+
<span class="ef-understanding__state">Understood</span>
|
|
28
|
+
<div><strong>Visit</strong><p>Dr. Smith visit occurred yesterday.</p></div>
|
|
29
|
+
<button type="button">Source</button>
|
|
30
|
+
</li>
|
|
31
|
+
</ol>
|
|
32
|
+
</section>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="ef-validation-summary" role="alert" tabindex="-1" aria-labelledby="validation-summary-title">
|
|
2
|
+
<div class="ef-validation-summary__icon" aria-hidden="true">!</div>
|
|
3
|
+
<div>
|
|
4
|
+
<h2 class="ef-validation-summary__title" id="validation-summary-title">There are 2 responses to review</h2>
|
|
5
|
+
<ul class="ef-validation-summary__list">
|
|
6
|
+
<li><a href="#question-security">Question 4 requires a response.</a></li>
|
|
7
|
+
<li><a href="#question-allocation">Question 8 must total 10 points.</a></li>
|
|
8
|
+
</ul>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<section class="ef-wizard" aria-labelledby="wizard-title">
|
|
2
|
+
<header class="ef-wizard__header">
|
|
3
|
+
<div><span class="ef-wizard__position">Step 2 of 4</span><h3 id="wizard-title">Review details</h3></div>
|
|
4
|
+
<progress value="2" max="4" aria-label="Workflow progress">2 of 4</progress>
|
|
5
|
+
</header>
|
|
6
|
+
<ol class="ef-wizard__steps" aria-label="Workflow steps">
|
|
7
|
+
<li data-state="complete"><span>1</span>Details</li>
|
|
8
|
+
<li data-state="current" aria-current="step"><span>2</span>Review</li>
|
|
9
|
+
<li><span>3</span>Validate</li>
|
|
10
|
+
<li><span>4</span>Publish</li>
|
|
11
|
+
</ol>
|
|
12
|
+
<div class="ef-wizard__body">
|
|
13
|
+
<p>Confirm the supplied information before continuing.</p>
|
|
14
|
+
</div>
|
|
15
|
+
<footer class="ef-wizard__actions">
|
|
16
|
+
<button type="button">Back</button>
|
|
17
|
+
<button type="button">Continue</button>
|
|
18
|
+
</footer>
|
|
19
|
+
</section>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<section class="ef-work-queue" aria-labelledby="work-queue-title">
|
|
2
|
+
<header class="ef-work-queue__header">
|
|
3
|
+
<div><span class="ef-component-kicker">Attention</span><h3 id="work-queue-title">Needs your attention</h3></div>
|
|
4
|
+
<span class="ef-status-lozenge">3 items</span>
|
|
5
|
+
</header>
|
|
6
|
+
<ol class="ef-work-queue__list">
|
|
7
|
+
<li class="ef-work-queue__item">
|
|
8
|
+
<div><strong>Invoice INV-1042 is overdue</strong><p>32 days overdue · Customer follow-up is available.</p></div>
|
|
9
|
+
<button type="button">Review</button>
|
|
10
|
+
</li>
|
|
11
|
+
<li class="ef-work-queue__item">
|
|
12
|
+
<div><strong>Write outcome is unknown</strong><p>Reconcile before retrying or assuming failure.</p></div>
|
|
13
|
+
<button type="button">Reconcile</button>
|
|
14
|
+
</li>
|
|
15
|
+
<li class="ef-work-queue__item">
|
|
16
|
+
<div><strong>Publication validation has warnings</strong><p>2 warnings require review before publishing.</p></div>
|
|
17
|
+
<button type="button">Inspect</button>
|
|
18
|
+
</li>
|
|
19
|
+
</ol>
|
|
20
|
+
</section>
|
package/dist/skins.css
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* Intentionally unlayered: opt-in skins override layered brand/token presentation primitives. */
|
|
2
|
+
[data-ef-skin="compact"] {
|
|
3
|
+
--ef-primitive-spacing-1: 3px;
|
|
4
|
+
--ef-primitive-spacing-2: 6px;
|
|
5
|
+
--ef-primitive-spacing-3: 9px;
|
|
6
|
+
--ef-primitive-spacing-4: 12px;
|
|
7
|
+
--ef-primitive-spacing-5: 18px;
|
|
8
|
+
--ef-primitive-spacing-6: 24px;
|
|
9
|
+
--ef-primitive-spacing-7: 36px;
|
|
10
|
+
--ef-primitive-spacing-8: 48px;
|
|
11
|
+
--ef-primitive-radius-small: 3px;
|
|
12
|
+
--ef-primitive-radius-medium: 6px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
[data-ef-skin="comfortable"] {
|
|
16
|
+
--ef-primitive-spacing-1: 5px;
|
|
17
|
+
--ef-primitive-spacing-2: 10px;
|
|
18
|
+
--ef-primitive-spacing-3: 15px;
|
|
19
|
+
--ef-primitive-spacing-4: 20px;
|
|
20
|
+
--ef-primitive-spacing-5: 30px;
|
|
21
|
+
--ef-primitive-spacing-6: 40px;
|
|
22
|
+
--ef-primitive-spacing-7: 60px;
|
|
23
|
+
--ef-primitive-spacing-8: 80px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
[data-ef-skin="square"] {
|
|
27
|
+
--ef-primitive-radius-small: 0;
|
|
28
|
+
--ef-primitive-radius-medium: 0;
|
|
29
|
+
}
|
package/dist/tokens.css
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Generated from tokens/echelon.tokens.json. Do not edit directly. */
|
|
2
|
+
@layer echelon.tokens {
|
|
3
|
+
:root {
|
|
4
|
+
--ef-primitive-color-carbon: #171a18;
|
|
5
|
+
--ef-primitive-color-forged-iron: #3a403c;
|
|
6
|
+
--ef-primitive-color-foundry-charcoal: #202421;
|
|
7
|
+
--ef-primitive-color-graphite: #686d68;
|
|
8
|
+
--ef-primitive-color-oxide-bronze: #905831;
|
|
9
|
+
--ef-primitive-color-oxide-bronze-light: #a87e5e;
|
|
10
|
+
--ef-primitive-color-parchment: #f2efe7;
|
|
11
|
+
--ef-primitive-color-stone: #e3e0d7;
|
|
12
|
+
--ef-primitive-color-verdigris: #47756b;
|
|
13
|
+
--ef-primitive-color-verdigris-light: #698d84;
|
|
14
|
+
--ef-primitive-font-family-display: Newsreader, Georgia, serif;
|
|
15
|
+
--ef-primitive-font-family-mono: "IBM Plex Mono", ui-monospace, monospace;
|
|
16
|
+
--ef-primitive-font-family-sans: Manrope, system-ui, sans-serif;
|
|
17
|
+
--ef-primitive-motion-duration-fast: 120ms;
|
|
18
|
+
--ef-primitive-motion-duration-instant: 0ms;
|
|
19
|
+
--ef-primitive-motion-duration-slow: 280ms;
|
|
20
|
+
--ef-primitive-motion-duration-standard: 180ms;
|
|
21
|
+
--ef-primitive-motion-easing-enter: cubic-bezier(0, 0, 0.2, 1);
|
|
22
|
+
--ef-primitive-motion-easing-exit: cubic-bezier(0.4, 0, 1, 1);
|
|
23
|
+
--ef-primitive-motion-easing-standard: cubic-bezier(0.2, 0, 0, 1);
|
|
24
|
+
--ef-primitive-radius-medium: 8px;
|
|
25
|
+
--ef-primitive-radius-none: 0px;
|
|
26
|
+
--ef-primitive-radius-pill: 999px;
|
|
27
|
+
--ef-primitive-radius-small: 4px;
|
|
28
|
+
--ef-primitive-spacing-1: 4px;
|
|
29
|
+
--ef-primitive-spacing-2: 8px;
|
|
30
|
+
--ef-primitive-spacing-3: 12px;
|
|
31
|
+
--ef-primitive-spacing-4: 16px;
|
|
32
|
+
--ef-primitive-spacing-5: 24px;
|
|
33
|
+
--ef-primitive-spacing-6: 32px;
|
|
34
|
+
--ef-primitive-spacing-7: 48px;
|
|
35
|
+
--ef-primitive-spacing-8: 64px;
|
|
36
|
+
}
|
|
37
|
+
:root, [data-ef-theme="light"] {
|
|
38
|
+
--ef-color-accent-primary: #905831;
|
|
39
|
+
--ef-color-accent-secondary: #47756b;
|
|
40
|
+
--ef-color-border-functional: #3a403c;
|
|
41
|
+
--ef-color-border-subtle: #e3e0d7;
|
|
42
|
+
--ef-color-control-active: #905831;
|
|
43
|
+
--ef-color-control-thumb: #f2efe7;
|
|
44
|
+
--ef-color-control-track: #686d68;
|
|
45
|
+
--ef-color-focus-gap: #f2efe7;
|
|
46
|
+
--ef-color-focus-ring: #171a18;
|
|
47
|
+
--ef-color-surface-inverse: #202421;
|
|
48
|
+
--ef-color-surface-inverse-secondary: #3a403c;
|
|
49
|
+
--ef-color-surface-primary: #f2efe7;
|
|
50
|
+
--ef-color-surface-secondary: #e3e0d7;
|
|
51
|
+
--ef-color-text-heading: #202421;
|
|
52
|
+
--ef-color-text-inverse: #f2efe7;
|
|
53
|
+
--ef-color-text-on-secondary-surface: #3a403c;
|
|
54
|
+
--ef-color-text-primary: #171a18;
|
|
55
|
+
--ef-color-text-secondary: #686d68;
|
|
56
|
+
}
|
|
57
|
+
@media (prefers-color-scheme: dark) {
|
|
58
|
+
:root:not([data-ef-theme]) {
|
|
59
|
+
--ef-color-accent-primary: #a87e5e;
|
|
60
|
+
--ef-color-accent-secondary: #698d84;
|
|
61
|
+
--ef-color-border-functional: #e3e0d7;
|
|
62
|
+
--ef-color-border-subtle: #3a403c;
|
|
63
|
+
--ef-color-control-active: #a87e5e;
|
|
64
|
+
--ef-color-control-thumb: #f2efe7;
|
|
65
|
+
--ef-color-control-track: #686d68;
|
|
66
|
+
--ef-color-focus-gap: #171a18;
|
|
67
|
+
--ef-color-focus-ring: #f2efe7;
|
|
68
|
+
--ef-color-surface-inverse: #f2efe7;
|
|
69
|
+
--ef-color-surface-inverse-secondary: #e3e0d7;
|
|
70
|
+
--ef-color-surface-primary: #171a18;
|
|
71
|
+
--ef-color-surface-secondary: #202421;
|
|
72
|
+
--ef-color-text-heading: #f2efe7;
|
|
73
|
+
--ef-color-text-inverse: #171a18;
|
|
74
|
+
--ef-color-text-on-secondary-surface: #f2efe7;
|
|
75
|
+
--ef-color-text-primary: #f2efe7;
|
|
76
|
+
--ef-color-text-secondary: #e3e0d7;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
[data-ef-theme="dark"] {
|
|
80
|
+
--ef-color-accent-primary: #a87e5e;
|
|
81
|
+
--ef-color-accent-secondary: #698d84;
|
|
82
|
+
--ef-color-border-functional: #e3e0d7;
|
|
83
|
+
--ef-color-border-subtle: #3a403c;
|
|
84
|
+
--ef-color-control-active: #a87e5e;
|
|
85
|
+
--ef-color-control-thumb: #f2efe7;
|
|
86
|
+
--ef-color-control-track: #686d68;
|
|
87
|
+
--ef-color-focus-gap: #171a18;
|
|
88
|
+
--ef-color-focus-ring: #f2efe7;
|
|
89
|
+
--ef-color-surface-inverse: #f2efe7;
|
|
90
|
+
--ef-color-surface-inverse-secondary: #e3e0d7;
|
|
91
|
+
--ef-color-surface-primary: #171a18;
|
|
92
|
+
--ef-color-surface-secondary: #202421;
|
|
93
|
+
--ef-color-text-heading: #f2efe7;
|
|
94
|
+
--ef-color-text-inverse: #171a18;
|
|
95
|
+
--ef-color-text-on-secondary-surface: #f2efe7;
|
|
96
|
+
--ef-color-text-primary: #f2efe7;
|
|
97
|
+
--ef-color-text-secondary: #e3e0d7;
|
|
98
|
+
}
|
|
99
|
+
}
|
package/docs/BRANDING.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Forma Branding and Skinning
|
|
2
|
+
|
|
3
|
+
Forma treats branding and skinning as separate presentation contracts.
|
|
4
|
+
|
|
5
|
+
- A **brand** changes identity and brand-level design tokens.
|
|
6
|
+
- A **skin** changes presentation density/shape without changing identity.
|
|
7
|
+
- Neither contract owns application behavior or domain state.
|
|
8
|
+
|
|
9
|
+
## Brand Manifest
|
|
10
|
+
|
|
11
|
+
Create a JSON manifest that conforms to `schemas/brand-manifest.schema.json`.
|
|
12
|
+
Repository-owned manifests live in `brands/*.brand.json`.
|
|
13
|
+
|
|
14
|
+
A manifest contains a stable brand ID, identity metadata, optional typography
|
|
15
|
+
and shape values, complete light/dark semantic colors, and optional application
|
|
16
|
+
terminology.
|
|
17
|
+
|
|
18
|
+
The manifest is data. Do not place CSS, script, credentials, or executable
|
|
19
|
+
configuration in it.
|
|
20
|
+
|
|
21
|
+
## Build
|
|
22
|
+
|
|
23
|
+
Compile all repository brand manifests with:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm run brands:build
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The F# compiler writes static files to:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
dist/brands/<brand-id>.css
|
|
33
|
+
dist/brands/index.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Invalid six-digit colors, unsafe font values, invalid dimensions, unsupported
|
|
37
|
+
schema versions, or insufficient required contrast fail the build.
|
|
38
|
+
|
|
39
|
+
## Load and apply a brand
|
|
40
|
+
|
|
41
|
+
Load Forma first, then the desired generated brand CSS:
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<link rel="stylesheet" href="/forma/all.css">
|
|
45
|
+
<link rel="stylesheet" href="/forma/brands/acme.css">
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Apply the brand at the document root:
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<html data-ef-brand="acme">
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
or to a subtree:
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<section data-ef-brand="acme">
|
|
58
|
+
<!-- canonical Forma markup -->
|
|
59
|
+
</section>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Because brands override the same documented `--ef-*` variables consumed by
|
|
63
|
+
Forma, canonical component markup does not change.
|
|
64
|
+
|
|
65
|
+
Multiple brand scopes can coexist on one page. This is useful for previews,
|
|
66
|
+
embedded products, partner portals, accessibility comparison, and design-system
|
|
67
|
+
testing.
|
|
68
|
+
|
|
69
|
+
## Themes
|
|
70
|
+
|
|
71
|
+
Forma still uses `data-ef-theme="light"` and `data-ef-theme="dark"`.
|
|
72
|
+
The brand compiler emits both semantic maps and also honors operating-system dark
|
|
73
|
+
preference when an explicit theme is absent.
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<section data-ef-brand="acme" data-ef-theme="dark">
|
|
77
|
+
...
|
|
78
|
+
</section>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Theme selection or persistence at runtime belongs to the consuming application,
|
|
82
|
+
normally through Limen. Forma only supplies the static presentation contract.
|
|
83
|
+
|
|
84
|
+
## Skins
|
|
85
|
+
|
|
86
|
+
`dist/skins.css` defines the initial zero-runtime skin presets:
|
|
87
|
+
|
|
88
|
+
- `compact`
|
|
89
|
+
- `comfortable`
|
|
90
|
+
- `square`
|
|
91
|
+
|
|
92
|
+
Use a skin independently of brand:
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<main data-ef-brand="acme" data-ef-skin="compact">
|
|
96
|
+
...
|
|
97
|
+
</main>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
A skin must not change labels, identity, permissions, workflow, validation,
|
|
101
|
+
scoring, or domain state.
|
|
102
|
+
|
|
103
|
+
## Terminology and assets
|
|
104
|
+
|
|
105
|
+
Brand terminology and identity assets are intentionally not converted into CSS.
|
|
106
|
+
Applications/build tooling consume those fields because they affect content and
|
|
107
|
+
metadata rather than component presentation.
|
|
108
|
+
|
|
109
|
+
This keeps a strong boundary:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
Brand Manifest
|
|
113
|
+
├─ identity / assets / terms -> application/build adapters
|
|
114
|
+
└─ presentation -> BrandCompiler -> scoped CSS variables
|
|
115
|
+
|
|
116
|
+
Skin
|
|
117
|
+
└─ presentation only -> skins.css
|
|
118
|
+
|
|
119
|
+
Application / Limen
|
|
120
|
+
└─ runtime selection, persistence, remote loading, preview behavior
|
|
121
|
+
|
|
122
|
+
Ordo / domain
|
|
123
|
+
└─ legal state and transitions
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Accessibility
|
|
127
|
+
|
|
128
|
+
The brand compiler currently validates the relationships that are most likely to
|
|
129
|
+
make a branded interface unreadable: primary and secondary text, text on
|
|
130
|
+
secondary surfaces, inverse text, primary/secondary accents, and focus rings.
|
|
131
|
+
|
|
132
|
+
A failed brand does not get a best-effort output. Fix the manifest and compile
|
|
133
|
+
again.
|
|
134
|
+
|
|
135
|
+
Forced-colors behavior and non-color state cues remain part of Forma's existing
|
|
136
|
+
component accessibility contract.
|
|
137
|
+
|
|
138
|
+
## White-labeling rules for applications
|
|
139
|
+
|
|
140
|
+
Applications should not fork Forma component CSS just to implement customer
|
|
141
|
+
branding. Add or update a Brand Manifest and consume its generated CSS.
|
|
142
|
+
|
|
143
|
+
Arbitrary customer CSS is not the supported extension mechanism. Internal
|
|
144
|
+
component structure is not a theming API.
|
|
145
|
+
|
|
146
|
+
If a required presentation concept cannot be expressed through the current
|
|
147
|
+
manifest/token contract, record the gap in Forma and extend the public contract
|
|
148
|
+
deliberately.
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@echelon-foundry/design-system",
|
|
3
|
+
"version": "0.2.0-main.15.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Zero-runtime HTML and CSS design system for Echelon Foundry applications.",
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"./dist/*.css",
|
|
9
|
+
"./dist/brands/*.css"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
"./tokens.css": "./dist/tokens.css",
|
|
13
|
+
"./foundations.css": "./dist/foundations.css",
|
|
14
|
+
"./components.css": "./dist/components.css",
|
|
15
|
+
"./all.css": "./dist/all.css",
|
|
16
|
+
"./patterns/*": "./dist/patterns/*",
|
|
17
|
+
"./assessment.css": "./dist/assessment.css",
|
|
18
|
+
"./skins.css": "./dist/skins.css",
|
|
19
|
+
"./brands/*": "./dist/brands/*",
|
|
20
|
+
"./brand-manifest.schema.json": "./schemas/brand-manifest.schema.json"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"patterns",
|
|
25
|
+
"schemas/brand-manifest.schema.json",
|
|
26
|
+
"docs/BRANDING.md"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"clean": "node tools/clean.mjs",
|
|
30
|
+
"tokens:build": "dotnet run --project tools/TokenCompiler/TokenCompiler.fsproj -- tokens/echelon.tokens.json dist/tokens.css",
|
|
31
|
+
"assets:build": "node tools/build-assets.mjs",
|
|
32
|
+
"build": "npm run clean && npm run tokens:build && npm run brands:build && npm run assets:build",
|
|
33
|
+
"test:tokens": "node --test tests/token-compiler.test.mjs",
|
|
34
|
+
"test:browser": "playwright test",
|
|
35
|
+
"test:a11y": "playwright test tests/browser/accessibility.spec.mjs",
|
|
36
|
+
"test:runtime": "node --test tests/zero-runtime.test.mjs",
|
|
37
|
+
"check": "npm run build && npm run test:tokens && npm run test:brands && npm run test:runtime && npm run test:package && npm run test:browser",
|
|
38
|
+
"site:build": "npm run build && node tools/build-site.mjs",
|
|
39
|
+
"site:test": "node --test tests/site-build.test.mjs",
|
|
40
|
+
"site:check": "npm run site:build && npm run site:test && npm run site:mobile",
|
|
41
|
+
"site:mobile": "playwright test --config=playwright.site.config.mjs",
|
|
42
|
+
"pack:verify": "npm run build && npm pack --dry-run",
|
|
43
|
+
"test:package": "node --test tests/package-contract.test.mjs",
|
|
44
|
+
"brands:build": "dotnet run --project tools/BrandCompiler/BrandCompiler.fsproj -- brands dist/brands",
|
|
45
|
+
"test:brands": "node --test tests/brand-compiler.test.mjs"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@axe-core/playwright": "4.13.0",
|
|
49
|
+
"@playwright/test": "1.63.0"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public",
|
|
53
|
+
"provenance": true
|
|
54
|
+
},
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/kemiller2002/forma.git"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://forma.echelonfoundry.com/"
|
|
60
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<aside class="ef-alert" role="status" data-tone="attention" data-ef-motion-weight="standard" data-ef-motion-entry>
|
|
2
|
+
<div class="ef-alert__icon" aria-hidden="true">!</div>
|
|
3
|
+
<div>
|
|
4
|
+
<h3 class="ef-alert__title">Review required</h3>
|
|
5
|
+
<p>Two values changed since this view was loaded. No update has been applied.</p>
|
|
6
|
+
</div>
|
|
7
|
+
<button type="button">Review changes</button>
|
|
8
|
+
</aside>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<fieldset class="ef-allocation">
|
|
2
|
+
<legend class="ef-allocation__legend">Distribute 10 points across these priorities.</legend>
|
|
3
|
+
|
|
4
|
+
<div class="ef-allocation__items">
|
|
5
|
+
<label class="ef-allocation__item">
|
|
6
|
+
<span>Reliability</span>
|
|
7
|
+
<input type="number" name="reliability" min="0" max="10" step="1" value="4" inputmode="numeric">
|
|
8
|
+
</label>
|
|
9
|
+
<label class="ef-allocation__item">
|
|
10
|
+
<span>Delivery speed</span>
|
|
11
|
+
<input type="number" name="delivery-speed" min="0" max="10" step="1" value="3" inputmode="numeric">
|
|
12
|
+
</label>
|
|
13
|
+
<label class="ef-allocation__item">
|
|
14
|
+
<span>Cost efficiency</span>
|
|
15
|
+
<input type="number" name="cost-efficiency" min="0" max="10" step="1" value="3" inputmode="numeric">
|
|
16
|
+
</label>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="ef-allocation__total" data-ef-state="valid" aria-live="polite" aria-atomic="true">
|
|
20
|
+
<span>Total</span>
|
|
21
|
+
<output>10 / 10</output>
|
|
22
|
+
</div>
|
|
23
|
+
<p class="ef-field__description">Limen validates the aggregate total and renders the authoritative state.</p>
|
|
24
|
+
</fieldset>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Feature Manifest — Assessment and workflow patterns
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Provide reusable semantic markup and CSS for assessment, decision, validation, and Ordo-style unresolved-work presentation without embedding application behavior.
|
|
6
|
+
|
|
7
|
+
## Ownership
|
|
8
|
+
|
|
9
|
+
- State, including presentation state: native radio/checkbox/number/progress state plus application-rendered `data-ef-*` state
|
|
10
|
+
- Transitions / commands / messages: native HTML events only for declarative controls; ranking/allocation/rule-builder behavior belongs to Limen/application code
|
|
11
|
+
- Invariants and guards: semantic form grouping, accessible names, non-color state cues, special answer separation, zero-runtime boundary
|
|
12
|
+
- Capabilities / authority: none — Signal/Ordo/application owns applicability, scoring, validation, capabilities, obligations, and completion
|
|
13
|
+
- Important effects and effect contracts: none
|
|
14
|
+
|
|
15
|
+
## Interfaces
|
|
16
|
+
|
|
17
|
+
### Declarative patterns
|
|
18
|
+
|
|
19
|
+
- `.ef-question`
|
|
20
|
+
- `.ef-ordinal-scale`
|
|
21
|
+
- `.ef-choice-group`
|
|
22
|
+
- `.ef-special-choices`
|
|
23
|
+
- `.ef-validation-message`
|
|
24
|
+
- `.ef-validation-summary`
|
|
25
|
+
- `.ef-survey-progress`
|
|
26
|
+
- `.ef-obligation-panel`
|
|
27
|
+
- `.ef-binary-choice`
|
|
28
|
+
- `.ef-semantic-differential`
|
|
29
|
+
- `.ef-symbol-rating`
|
|
30
|
+
- `.ef-numeric-stepper`
|
|
31
|
+
- `.ef-range-entry`
|
|
32
|
+
- `.ef-choice--media`
|
|
33
|
+
- `.ef-matrix`
|
|
34
|
+
- `.ef-pairwise`
|
|
35
|
+
- `.ef-best-worst`
|
|
36
|
+
- `.ef-hierarchy`
|
|
37
|
+
|
|
38
|
+
### Behavior-required visual contracts
|
|
39
|
+
|
|
40
|
+
- `.ef-ranking`
|
|
41
|
+
- `.ef-allocation` aggregate validation
|
|
42
|
+
- `.ef-rule-builder`
|
|
43
|
+
- graphical dual-thumb range interaction
|
|
44
|
+
- best/worst cross-column exclusion
|
|
45
|
+
- multi-choice cardinality/exclusive-option enforcement
|
|
46
|
+
- matrix global constraints
|
|
47
|
+
- hierarchical cascading/parent-child policy
|
|
48
|
+
|
|
49
|
+
## Tests and verification
|
|
50
|
+
|
|
51
|
+
- browser behavior: `tests/browser/assessment.spec.mjs`
|
|
52
|
+
- automated accessibility: `tests/browser/accessibility.spec.mjs`
|
|
53
|
+
- zero-runtime boundary: `tests/zero-runtime.test.mjs`
|
|
54
|
+
- responsive ordinal presentation: browser test at narrow viewport
|
|
55
|
+
|
|
56
|
+
## Dependencies
|
|
57
|
+
|
|
58
|
+
- allowed direct dependencies: semantic tokens, foundations, assessment CSS
|
|
59
|
+
- required composition context: consuming application supplies domain state and Limen behavior where documented
|
|
60
|
+
|
|
61
|
+
## Modification boundaries
|
|
62
|
+
|
|
63
|
+
Normal:
|
|
64
|
+
|
|
65
|
+
- `patterns/question.html`
|
|
66
|
+
- `patterns/ordinal-scale.html`
|
|
67
|
+
- `patterns/choice-group.html`
|
|
68
|
+
- `patterns/special-choice.html`
|
|
69
|
+
- `patterns/validation-*.html`
|
|
70
|
+
- `patterns/survey-progress.html`
|
|
71
|
+
- `patterns/ranking.html`
|
|
72
|
+
- `patterns/allocation.html`
|
|
73
|
+
- `patterns/rule-builder.html`
|
|
74
|
+
- `patterns/obligation-panel.html`
|
|
75
|
+
- `src/styles/assessment.css`
|
|
76
|
+
- related tests/docs
|
|
77
|
+
|
|
78
|
+
Escalation required:
|
|
79
|
+
|
|
80
|
+
- adding executable behavior to the package;
|
|
81
|
+
- creating scoring semantics from presentation;
|
|
82
|
+
- replacing native radio/checkbox semantics;
|
|
83
|
+
- collapsing Unknown, Not Applicable, and ordinal values into one visual continuum;
|
|
84
|
+
- making drag the only ranking path;
|
|
85
|
+
- using a checkbox switch where Unanswered must remain distinct from false;
|
|
86
|
+
- treating NPS/Likert/star/icon labels as scoring authority;
|
|
87
|
+
- requiring horizontal scrolling as the only matrix mobile interaction.
|
|
88
|
+
|
|
89
|
+
## Maintenance
|
|
90
|
+
|
|
91
|
+
- Owner: Echelon Foundry design system
|
|
92
|
+
- First demanding consumer: Echelon Signal
|
|
93
|
+
- Last checked against implementation: 2026-09-22
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<fieldset class="ef-best-worst">
|
|
2
|
+
<legend class="ef-best-worst__legend">Choose the most important and least important item.</legend>
|
|
3
|
+
<p class="ef-field__description">One item must be Best and a different item must be Worst. Limen enforces the cross-column constraint.</p>
|
|
4
|
+
|
|
5
|
+
<div class="ef-best-worst__header" aria-hidden="true">
|
|
6
|
+
<span>Option</span><span>Best</span><span>Worst</span>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="ef-best-worst__row">
|
|
10
|
+
<span id="bw-reliability">Reliability</span>
|
|
11
|
+
<label><input type="radio" name="best" value="reliability" aria-labelledby="bw-reliability bw-best-label"><span class="ef-sr-only" id="bw-best-label">Best</span></label>
|
|
12
|
+
<label><input type="radio" name="worst" value="reliability" aria-labelledby="bw-reliability bw-worst-label"><span class="ef-sr-only" id="bw-worst-label">Worst</span></label>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="ef-best-worst__row">
|
|
15
|
+
<span id="bw-speed">Delivery speed</span>
|
|
16
|
+
<label><input type="radio" name="best" value="speed" aria-label="Delivery speed, Best"></label>
|
|
17
|
+
<label><input type="radio" name="worst" value="speed" aria-label="Delivery speed, Worst"></label>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<p class="ef-best-worst__status" data-ef-state="incomplete" aria-live="polite">Choose one Best and one Worst.</p>
|
|
21
|
+
</fieldset>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<fieldset class="ef-binary-choice">
|
|
2
|
+
<legend class="ef-binary-choice__legend">Is automated deployment currently in use?</legend>
|
|
3
|
+
<p class="ef-field__description" id="binary-choice-help">Choose one response. An unanswered question remains distinct from No.</p>
|
|
4
|
+
|
|
5
|
+
<div class="ef-binary-choice__options" aria-describedby="binary-choice-help">
|
|
6
|
+
<label class="ef-binary-option">
|
|
7
|
+
<input type="radio" name="automated-deployment" value="yes" required>
|
|
8
|
+
<span>Yes</span>
|
|
9
|
+
</label>
|
|
10
|
+
<label class="ef-binary-option">
|
|
11
|
+
<input type="radio" name="automated-deployment" value="no">
|
|
12
|
+
<span>No</span>
|
|
13
|
+
</label>
|
|
14
|
+
</div>
|
|
15
|
+
</fieldset>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<label class="ef-checkbox" data-ef-motion-weight="standard">
|
|
2
|
+
<input
|
|
3
|
+
class="ef-checkbox__input"
|
|
4
|
+
type="checkbox"
|
|
5
|
+
name="audit-log"
|
|
6
|
+
value="enabled"
|
|
7
|
+
aria-describedby="audit-log-description">
|
|
8
|
+
<span class="ef-checkbox__box" aria-hidden="true"></span>
|
|
9
|
+
<span class="ef-checkbox__text">
|
|
10
|
+
<span class="ef-checkbox__label">Record audit events</span>
|
|
11
|
+
<span class="ef-checkbox__description" id="audit-log-description">Include this action in the audit trail.</span>
|
|
12
|
+
</span>
|
|
13
|
+
</label>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<fieldset class="ef-choice-group">
|
|
2
|
+
<legend class="ef-choice-group__legend">Which environment is most representative?</legend>
|
|
3
|
+
|
|
4
|
+
<label class="ef-choice">
|
|
5
|
+
<input type="radio" name="environment" value="cloud" required>
|
|
6
|
+
<span class="ef-choice__content">
|
|
7
|
+
<strong>Cloud</strong>
|
|
8
|
+
<span>Primarily hosted cloud infrastructure.</span>
|
|
9
|
+
</span>
|
|
10
|
+
</label>
|
|
11
|
+
|
|
12
|
+
<label class="ef-choice">
|
|
13
|
+
<input type="radio" name="environment" value="hybrid">
|
|
14
|
+
<span class="ef-choice__content">
|
|
15
|
+
<strong>Hybrid</strong>
|
|
16
|
+
<span>Cloud and on-premises infrastructure.</span>
|
|
17
|
+
</span>
|
|
18
|
+
</label>
|
|
19
|
+
|
|
20
|
+
<label class="ef-choice">
|
|
21
|
+
<input type="radio" name="environment" value="on-premises">
|
|
22
|
+
<span class="ef-choice__content">
|
|
23
|
+
<strong>On-premises</strong>
|
|
24
|
+
<span>Primarily locally hosted infrastructure.</span>
|
|
25
|
+
</span>
|
|
26
|
+
</label>
|
|
27
|
+
</fieldset>
|