@aiaiai-pt/design-system 0.49.0 → 0.50.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.
- package/components/CaminhoStateChip.svelte +89 -0
- package/components/CaminhoStateChip.svelte.d.ts +38 -0
- package/components/KpiRegister.svelte +158 -0
- package/components/KpiRegister.svelte.d.ts +46 -0
- package/components/SealChip.svelte +133 -0
- package/components/SealChip.svelte.d.ts +41 -0
- package/components/index.d.ts +3 -0
- package/components/index.js +6 -0
- package/package.json +1 -1
- package/tokens/components.css +15 -0
- package/tokens/themes/ubp.css +41 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component CaminhoStateChip
|
|
3
|
+
|
|
4
|
+
A Badge-variant chip rendering the four verification states as WORDS.
|
|
5
|
+
|
|
6
|
+
The four-state machine (H1 Slice 6 / verification surface):
|
|
7
|
+
to-verify — horizon not yet reached; awaiting observation
|
|
8
|
+
verified — observed and confirmed at the horizon
|
|
9
|
+
not-verified — horizon passed; observation explicitly absent
|
|
10
|
+
not-done — path discarded; verification not applicable
|
|
11
|
+
|
|
12
|
+
States are rendered as English words, not icons or abbreviations.
|
|
13
|
+
Screen readers announce the full word; no icon-only encoding.
|
|
14
|
+
|
|
15
|
+
Built over --badge-* component tokens. English-only canonical state names
|
|
16
|
+
(shared-by-default doctrine — no urban vocabulary here).
|
|
17
|
+
|
|
18
|
+
H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
19
|
+
|
|
20
|
+
@example All four states
|
|
21
|
+
<CaminhoStateChip state="to-verify" />
|
|
22
|
+
<CaminhoStateChip state="verified" />
|
|
23
|
+
<CaminhoStateChip state="not-verified" />
|
|
24
|
+
<CaminhoStateChip state="not-done" />
|
|
25
|
+
-->
|
|
26
|
+
<script>
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {'to-verify' | 'verified' | 'not-verified' | 'not-done'} CaminhoState
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
/** @type {CaminhoState} */
|
|
33
|
+
state,
|
|
34
|
+
/** @type {string} */
|
|
35
|
+
class: className = '',
|
|
36
|
+
...rest
|
|
37
|
+
} = $props();
|
|
38
|
+
|
|
39
|
+
/** @type {Record<CaminhoState, string>} */
|
|
40
|
+
const STATE_LABELS = {
|
|
41
|
+
'to-verify': 'To verify',
|
|
42
|
+
'verified': 'Verified',
|
|
43
|
+
'not-verified': 'Not verified',
|
|
44
|
+
'not-done': 'Not done',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const label = $derived(STATE_LABELS[state] ?? state);
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<span class="caminho-chip caminho-chip-{state} {className}" {...rest}>
|
|
51
|
+
{label}
|
|
52
|
+
</span>
|
|
53
|
+
|
|
54
|
+
<style>
|
|
55
|
+
.caminho-chip {
|
|
56
|
+
display: inline-flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
font-family: var(--badge-font);
|
|
59
|
+
font-size: var(--badge-size);
|
|
60
|
+
letter-spacing: var(--badge-tracking);
|
|
61
|
+
border-radius: var(--badge-radius);
|
|
62
|
+
padding: var(--badge-padding-y) var(--badge-padding-x);
|
|
63
|
+
white-space: nowrap;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* to-verify: informational — horizon pending */
|
|
67
|
+
.caminho-chip-to-verify {
|
|
68
|
+
background: var(--color-info-subtle);
|
|
69
|
+
color: var(--color-info);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* verified: success — observation confirmed */
|
|
73
|
+
.caminho-chip-verified {
|
|
74
|
+
background: var(--color-success-subtle);
|
|
75
|
+
color: var(--color-success);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* not-verified: destructive — horizon passed, no observation */
|
|
79
|
+
.caminho-chip-not-verified {
|
|
80
|
+
background: var(--color-destructive-subtle);
|
|
81
|
+
color: var(--color-destructive);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* not-done: neutral — path discarded, verification not applicable */
|
|
85
|
+
.caminho-chip-not-done {
|
|
86
|
+
background: var(--badge-neutral-bg);
|
|
87
|
+
color: var(--badge-neutral-text);
|
|
88
|
+
}
|
|
89
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export default CaminhoStateChip;
|
|
2
|
+
type CaminhoStateChip = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* CaminhoStateChip
|
|
8
|
+
*
|
|
9
|
+
* A Badge-variant chip rendering the four verification states as WORDS.
|
|
10
|
+
*
|
|
11
|
+
* The four-state machine (H1 Slice 6 / verification surface):
|
|
12
|
+
* to-verify — horizon not yet reached; awaiting observation
|
|
13
|
+
* verified — observed and confirmed at the horizon
|
|
14
|
+
* not-verified — horizon passed; observation explicitly absent
|
|
15
|
+
* not-done — path discarded; verification not applicable
|
|
16
|
+
*
|
|
17
|
+
* States are rendered as English words, not icons or abbreviations.
|
|
18
|
+
* Screen readers announce the full word; no icon-only encoding.
|
|
19
|
+
*
|
|
20
|
+
* Built over --badge-* component tokens. English-only canonical state names
|
|
21
|
+
* (shared-by-default doctrine — no urban vocabulary here).
|
|
22
|
+
*
|
|
23
|
+
* H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
24
|
+
*
|
|
25
|
+
* @example All four states
|
|
26
|
+
* <CaminhoStateChip state="to-verify" />
|
|
27
|
+
* <CaminhoStateChip state="verified" />
|
|
28
|
+
* <CaminhoStateChip state="not-verified" />
|
|
29
|
+
* <CaminhoStateChip state="not-done" />
|
|
30
|
+
*/
|
|
31
|
+
declare const CaminhoStateChip: import("svelte").Component<{
|
|
32
|
+
state: any;
|
|
33
|
+
class?: string;
|
|
34
|
+
} & Record<string, any>, {}, "">;
|
|
35
|
+
type $$ComponentProps = {
|
|
36
|
+
state: any;
|
|
37
|
+
class?: string;
|
|
38
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component KpiRegister
|
|
3
|
+
|
|
4
|
+
A StatCard-variant register for KPI values where a measured reading
|
|
5
|
+
visually out-ranks a projected value (larger display text, higher position
|
|
6
|
+
in the visual hierarchy).
|
|
7
|
+
|
|
8
|
+
BOUNDARY ELEMENT: throws when a projected value is provided without a seal.
|
|
9
|
+
This is the enforcement point for the seal rule (prd.md §5.1): no unsealed
|
|
10
|
+
projected value may cross this boundary into the rendering surface. The throw
|
|
11
|
+
is unconditional — production code must never pass an unsealed projected value
|
|
12
|
+
here; the guard exists to catch contract violations early.
|
|
13
|
+
|
|
14
|
+
H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
15
|
+
References sistema#67 for .type-overline (--type-overline-* tokens).
|
|
16
|
+
|
|
17
|
+
@example Measured only
|
|
18
|
+
<KpiRegister label="ENERGY OUTPUT" measuredValue="2.3 MW" />
|
|
19
|
+
|
|
20
|
+
@example With sealed projected value
|
|
21
|
+
<KpiRegister
|
|
22
|
+
label="ENERGY OUTPUT"
|
|
23
|
+
measuredValue="2.3 MW"
|
|
24
|
+
projectedValue="4.1 MW"
|
|
25
|
+
projectedSeal={{ evidence: 'projected', stale: false }}
|
|
26
|
+
/>
|
|
27
|
+
-->
|
|
28
|
+
<script>
|
|
29
|
+
import SealChip from './SealChip.svelte';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {'measured' | 'inferred' | 'projected'} EvidenceState
|
|
33
|
+
* @typedef {{ evidence: EvidenceState; stale: boolean }} SealRef
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
/** @type {string} — overline label (KPI name / dimension) */
|
|
38
|
+
label,
|
|
39
|
+
/** @type {string} — the measured (authoritative) value; always required */
|
|
40
|
+
measuredValue,
|
|
41
|
+
/** @type {string | undefined} — a projected value for the same KPI */
|
|
42
|
+
projectedValue = undefined,
|
|
43
|
+
/**
|
|
44
|
+
* @type {SealRef | undefined}
|
|
45
|
+
* Seal metadata for the projected value. REQUIRED when projectedValue
|
|
46
|
+
* is provided. The boundary guard throws when this is absent.
|
|
47
|
+
* Obtain from assignSeal() in @aiaiai-pt/widget-system/core.
|
|
48
|
+
*/
|
|
49
|
+
projectedSeal = undefined,
|
|
50
|
+
/** @type {string} */
|
|
51
|
+
class: className = '',
|
|
52
|
+
...rest
|
|
53
|
+
} = $props();
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* BOUNDARY GUARD — the seal rule enforcement point.
|
|
57
|
+
*
|
|
58
|
+
* Runs synchronously at component initialisation. Also runs reactively
|
|
59
|
+
* via $effect so prop changes after mount are also checked.
|
|
60
|
+
*
|
|
61
|
+
* Throws unconditionally when projectedValue is provided without a seal;
|
|
62
|
+
* production surfaces must never reach this state (prd.md §5.1 #3:
|
|
63
|
+
* "the product, deterministically" assigns the seal — a missing seal
|
|
64
|
+
* means the caller skipped assignSeal, which is the defect).
|
|
65
|
+
*/
|
|
66
|
+
if (projectedValue !== undefined && projectedSeal == null) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
'[KpiRegister] A projected value must carry a seal. ' +
|
|
69
|
+
'Provide the `projectedSeal` prop (result of assignSeal()). ' +
|
|
70
|
+
'An unsealed projected value may not cross this boundary.',
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$effect(() => {
|
|
75
|
+
if (projectedValue !== undefined && projectedSeal == null) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
'[KpiRegister] A projected value must carry a seal. ' +
|
|
78
|
+
'Provide the `projectedSeal` prop (result of assignSeal()). ' +
|
|
79
|
+
'An unsealed projected value may not cross this boundary.',
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<div class="kpi-register {className}" {...rest}>
|
|
86
|
+
<!--
|
|
87
|
+
Overline label — micro-heading above the value.
|
|
88
|
+
Uses --type-overline-* tokens directly (sistema#67 .type-overline utility).
|
|
89
|
+
-->
|
|
90
|
+
<span class="kpi-register-label">{label}</span>
|
|
91
|
+
|
|
92
|
+
<!--
|
|
93
|
+
Measured value: the authoritative reading.
|
|
94
|
+
Display-scale type — visually out-ranks the projected row.
|
|
95
|
+
-->
|
|
96
|
+
<div class="kpi-register-measured">
|
|
97
|
+
<span class="kpi-register-measured-value">{measuredValue}</span>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!--
|
|
101
|
+
Projected row: subordinate to the measured value (smaller type, lower position).
|
|
102
|
+
Only rendered when a sealed projected value is provided.
|
|
103
|
+
projectedSeal is guaranteed non-null here (boundary guard above).
|
|
104
|
+
-->
|
|
105
|
+
{#if projectedValue !== undefined && projectedSeal != null}
|
|
106
|
+
<div class="kpi-register-projected">
|
|
107
|
+
<SealChip
|
|
108
|
+
value={projectedValue}
|
|
109
|
+
evidence={projectedSeal.evidence}
|
|
110
|
+
stale={projectedSeal.stale}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
{/if}
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<style>
|
|
117
|
+
.kpi-register {
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
gap: var(--space-xs);
|
|
121
|
+
padding: var(--stat-padding);
|
|
122
|
+
border: var(--stat-border);
|
|
123
|
+
border-radius: var(--stat-radius);
|
|
124
|
+
background: var(--stat-bg);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* Overline label — consumes --type-overline-* tokens (sistema#67).
|
|
129
|
+
* Equivalent to the .type-overline utility class from utilities.css,
|
|
130
|
+
* declared here so the component is self-contained.
|
|
131
|
+
*/
|
|
132
|
+
.kpi-register-label {
|
|
133
|
+
font-family: var(--type-overline-font);
|
|
134
|
+
font-size: var(--type-overline-size);
|
|
135
|
+
font-weight: var(--type-overline-weight);
|
|
136
|
+
line-height: var(--type-overline-leading);
|
|
137
|
+
letter-spacing: var(--type-overline-tracking);
|
|
138
|
+
text-transform: uppercase;
|
|
139
|
+
color: var(--stat-label-color);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Measured value: display scale — the dominant visual element */
|
|
143
|
+
.kpi-register-measured-value {
|
|
144
|
+
font-family: var(--stat-value-font);
|
|
145
|
+
font-size: var(--stat-value-size);
|
|
146
|
+
font-weight: var(--stat-value-weight);
|
|
147
|
+
letter-spacing: var(--stat-value-tracking);
|
|
148
|
+
color: var(--color-text);
|
|
149
|
+
line-height: 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Projected row: body-scale — subordinate to the measured value */
|
|
153
|
+
.kpi-register-projected {
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
margin-top: var(--space-2xs);
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export default KpiRegister;
|
|
2
|
+
type KpiRegister = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* KpiRegister
|
|
8
|
+
*
|
|
9
|
+
* A StatCard-variant register for KPI values where a measured reading
|
|
10
|
+
* visually out-ranks a projected value (larger display text, higher position
|
|
11
|
+
* in the visual hierarchy).
|
|
12
|
+
*
|
|
13
|
+
* BOUNDARY ELEMENT: throws when a projected value is provided without a seal.
|
|
14
|
+
* This is the enforcement point for the seal rule (prd.md §5.1): no unsealed
|
|
15
|
+
* projected value may cross this boundary into the rendering surface. The throw
|
|
16
|
+
* is unconditional — production code must never pass an unsealed projected value
|
|
17
|
+
* here; the guard exists to catch contract violations early.
|
|
18
|
+
*
|
|
19
|
+
* H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
20
|
+
* References sistema#67 for .type-overline (--type-overline-* tokens).
|
|
21
|
+
*
|
|
22
|
+
* @example Measured only
|
|
23
|
+
* <KpiRegister label="ENERGY OUTPUT" measuredValue="2.3 MW" />
|
|
24
|
+
*
|
|
25
|
+
* @example With sealed projected value
|
|
26
|
+
* <KpiRegister
|
|
27
|
+
* label="ENERGY OUTPUT"
|
|
28
|
+
* measuredValue="2.3 MW"
|
|
29
|
+
* projectedValue="4.1 MW"
|
|
30
|
+
* projectedSeal={{ evidence: 'projected', stale: false }}
|
|
31
|
+
* />
|
|
32
|
+
*/
|
|
33
|
+
declare const KpiRegister: import("svelte").Component<{
|
|
34
|
+
label: any;
|
|
35
|
+
measuredValue: any;
|
|
36
|
+
projectedValue?: any;
|
|
37
|
+
projectedSeal?: any;
|
|
38
|
+
class?: string;
|
|
39
|
+
} & Record<string, any>, {}, "">;
|
|
40
|
+
type $$ComponentProps = {
|
|
41
|
+
label: any;
|
|
42
|
+
measuredValue: any;
|
|
43
|
+
projectedValue?: any;
|
|
44
|
+
projectedSeal?: any;
|
|
45
|
+
class?: string;
|
|
46
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component SealChip
|
|
3
|
+
|
|
4
|
+
Renders a value paired with its evidence seal as ONE phrase.
|
|
5
|
+
|
|
6
|
+
The seal must survive quotation: an sr-only span adjacent to the value
|
|
7
|
+
carries the evidence state word so copying or citing the value also
|
|
8
|
+
carries the seal (prd.md §5.1 #5: "a citation carries the seal of the
|
|
9
|
+
value it cites").
|
|
10
|
+
|
|
11
|
+
DS-owned evidence vocabulary: measured | inferred | projected.
|
|
12
|
+
Built over --badge-* + --seal-* component tokens.
|
|
13
|
+
|
|
14
|
+
H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
15
|
+
|
|
16
|
+
@example Measured reading
|
|
17
|
+
<SealChip value="2.3 MW" evidence="measured" />
|
|
18
|
+
|
|
19
|
+
@example Projected (future window)
|
|
20
|
+
<SealChip value="4.1 MW" evidence="projected" />
|
|
21
|
+
|
|
22
|
+
@example Stale seal (horizon has passed)
|
|
23
|
+
<SealChip value="1.9 MW" evidence="projected" stale />
|
|
24
|
+
-->
|
|
25
|
+
<script>
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {'measured' | 'inferred' | 'projected'} EvidenceState
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
let {
|
|
31
|
+
/** @type {string} — the formatted display value */
|
|
32
|
+
value,
|
|
33
|
+
/** @type {EvidenceState} — evidence state (from assignSeal) */
|
|
34
|
+
evidence,
|
|
35
|
+
/** @type {boolean} — true when the validity window has passed */
|
|
36
|
+
stale = false,
|
|
37
|
+
/** @type {string} */
|
|
38
|
+
class: className = '',
|
|
39
|
+
...rest
|
|
40
|
+
} = $props();
|
|
41
|
+
|
|
42
|
+
const EVIDENCE_LABELS = /** @type {Record<EvidenceState, string>} */ ({
|
|
43
|
+
measured: 'measured',
|
|
44
|
+
inferred: 'inferred',
|
|
45
|
+
projected: 'projected',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const evidenceLabel = $derived(EVIDENCE_LABELS[evidence] ?? evidence);
|
|
49
|
+
|
|
50
|
+
/** Label as spoken by AT and included in citations. */
|
|
51
|
+
const srLabel = $derived(stale ? `${evidenceLabel} (stale)` : evidenceLabel);
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<!--
|
|
55
|
+
The chip is one inline phrase. Structure:
|
|
56
|
+
[value text]
|
|
57
|
+
[visual badge — aria-hidden, not read by AT]
|
|
58
|
+
[sr-only span — read by AT; position:absolute but in DOM so text
|
|
59
|
+
travels with the value when the surrounding content is copied]
|
|
60
|
+
-->
|
|
61
|
+
<span
|
|
62
|
+
class="seal-chip seal-chip-{evidence} {stale ? 'seal-chip-stale' : ''} {className}"
|
|
63
|
+
{...rest}
|
|
64
|
+
>
|
|
65
|
+
<span class="seal-chip-value">{value}</span><!--
|
|
66
|
+
--><span class="seal-chip-badge" aria-hidden="true">{evidenceLabel}{stale ? ' (stale)' : ''}</span><!--
|
|
67
|
+
--><span class="seal-chip-sr-text"> {srLabel}</span>
|
|
68
|
+
</span>
|
|
69
|
+
|
|
70
|
+
<style>
|
|
71
|
+
.seal-chip {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: baseline;
|
|
74
|
+
gap: var(--space-xs);
|
|
75
|
+
position: relative;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Value text: no override — reads as its context's surrounding type */
|
|
79
|
+
.seal-chip-value {
|
|
80
|
+
/* intentionally empty */
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Visual evidence badge — seen by sighted users; hidden from AT */
|
|
84
|
+
.seal-chip-badge {
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
font-family: var(--badge-font);
|
|
88
|
+
font-size: var(--badge-size);
|
|
89
|
+
letter-spacing: var(--badge-tracking);
|
|
90
|
+
border-radius: var(--badge-radius);
|
|
91
|
+
padding: var(--badge-padding-y) var(--badge-padding-x);
|
|
92
|
+
white-space: nowrap;
|
|
93
|
+
line-height: 1;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Evidence-state colours via --seal-* component tokens */
|
|
97
|
+
.seal-chip-measured .seal-chip-badge {
|
|
98
|
+
color: var(--seal-measured-text);
|
|
99
|
+
background: var(--seal-measured-bg);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.seal-chip-inferred .seal-chip-badge {
|
|
103
|
+
color: var(--seal-inferred-text);
|
|
104
|
+
background: var(--seal-inferred-bg);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.seal-chip-projected .seal-chip-badge {
|
|
108
|
+
color: var(--seal-projected-text);
|
|
109
|
+
background: var(--seal-projected-bg);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Stale modifier: muted on all evidence states */
|
|
113
|
+
.seal-chip-stale .seal-chip-badge {
|
|
114
|
+
color: var(--seal-stale-text);
|
|
115
|
+
background: var(--seal-stale-bg);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* sr-only — visually hidden but present in the DOM so the text travels
|
|
120
|
+
* with the value when content is copied or cited (prd.md §5.1 #5).
|
|
121
|
+
* clip-path: inset(50%) is the modern replacement for deprecated clip: rect().
|
|
122
|
+
*/
|
|
123
|
+
.seal-chip-sr-text {
|
|
124
|
+
position: absolute;
|
|
125
|
+
width: 1px;
|
|
126
|
+
height: 1px;
|
|
127
|
+
padding: 0;
|
|
128
|
+
margin: -1px;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
clip-path: inset(50%);
|
|
131
|
+
white-space: nowrap;
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export default SealChip;
|
|
2
|
+
type SealChip = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* SealChip
|
|
8
|
+
*
|
|
9
|
+
* Renders a value paired with its evidence seal as ONE phrase.
|
|
10
|
+
*
|
|
11
|
+
* The seal must survive quotation: an sr-only span adjacent to the value
|
|
12
|
+
* carries the evidence state word so copying or citing the value also
|
|
13
|
+
* carries the seal (prd.md §5.1 #5: "a citation carries the seal of the
|
|
14
|
+
* value it cites").
|
|
15
|
+
*
|
|
16
|
+
* DS-owned evidence vocabulary: measured | inferred | projected.
|
|
17
|
+
* Built over --badge-* + --seal-* component tokens.
|
|
18
|
+
*
|
|
19
|
+
* H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
20
|
+
*
|
|
21
|
+
* @example Measured reading
|
|
22
|
+
* <SealChip value="2.3 MW" evidence="measured" />
|
|
23
|
+
*
|
|
24
|
+
* @example Projected (future window)
|
|
25
|
+
* <SealChip value="4.1 MW" evidence="projected" />
|
|
26
|
+
*
|
|
27
|
+
* @example Stale seal (horizon has passed)
|
|
28
|
+
* <SealChip value="1.9 MW" evidence="projected" stale />
|
|
29
|
+
*/
|
|
30
|
+
declare const SealChip: import("svelte").Component<{
|
|
31
|
+
value: any;
|
|
32
|
+
evidence: any;
|
|
33
|
+
stale?: boolean;
|
|
34
|
+
class?: string;
|
|
35
|
+
} & Record<string, any>, {}, "">;
|
|
36
|
+
type $$ComponentProps = {
|
|
37
|
+
value: any;
|
|
38
|
+
evidence: any;
|
|
39
|
+
stale?: boolean;
|
|
40
|
+
class?: string;
|
|
41
|
+
} & Record<string, any>;
|
package/components/index.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ export { default as Tag } from "./Tag.svelte";
|
|
|
3
3
|
export { default as Status } from "./Status.svelte";
|
|
4
4
|
export { default as Skeleton } from "./Skeleton.svelte";
|
|
5
5
|
export { default as KeyValue } from "./KeyValue.svelte";
|
|
6
|
+
export { default as SealChip } from "./SealChip.svelte";
|
|
7
|
+
export { default as KpiRegister } from "./KpiRegister.svelte";
|
|
8
|
+
export { default as CaminhoStateChip } from "./CaminhoStateChip.svelte";
|
|
6
9
|
export { default as Button } from "./Button.svelte";
|
|
7
10
|
export { default as Input } from "./Input.svelte";
|
|
8
11
|
export { default as Textarea } from "./Textarea.svelte";
|
package/components/index.js
CHANGED
|
@@ -15,6 +15,12 @@ export { default as Status } from "./Status.svelte";
|
|
|
15
15
|
export { default as Skeleton } from "./Skeleton.svelte";
|
|
16
16
|
export { default as KeyValue } from "./KeyValue.svelte";
|
|
17
17
|
|
|
18
|
+
// Evidence seal (H1 Slice 2 — westeuropeco/atelier-urban-workspace#57)
|
|
19
|
+
// DS-owned evidence vocabulary: measured | inferred | projected
|
|
20
|
+
export { default as SealChip } from "./SealChip.svelte";
|
|
21
|
+
export { default as KpiRegister } from "./KpiRegister.svelte";
|
|
22
|
+
export { default as CaminhoStateChip } from "./CaminhoStateChip.svelte";
|
|
23
|
+
|
|
18
24
|
// Form controls
|
|
19
25
|
export { default as Button } from "./Button.svelte";
|
|
20
26
|
export { default as Input } from "./Input.svelte";
|
package/package.json
CHANGED
package/tokens/components.css
CHANGED
|
@@ -248,6 +248,21 @@
|
|
|
248
248
|
--badge-neutral-bg: var(--color-surface-tertiary);
|
|
249
249
|
--badge-neutral-text: var(--color-text-secondary);
|
|
250
250
|
|
|
251
|
+
/* Evidence Seal — DS-owned vocabulary: measured | inferred | projected
|
|
252
|
+
H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
253
|
+
Defaults resolve via semantic color tokens so they work for every theme.
|
|
254
|
+
UBP dark scheme overrides below in tokens/themes/ubp.css to maintain
|
|
255
|
+
WCAG AA on the cool-steel dark surface (#0b182a). */
|
|
256
|
+
--seal-measured-text: var(--color-success);
|
|
257
|
+
--seal-measured-bg: var(--color-success-subtle);
|
|
258
|
+
--seal-inferred-text: var(--color-info);
|
|
259
|
+
--seal-inferred-bg: var(--color-info-subtle);
|
|
260
|
+
--seal-projected-text: var(--color-warning);
|
|
261
|
+
--seal-projected-bg: var(--color-warning-subtle);
|
|
262
|
+
/* stale modifier: muted regardless of base evidence state */
|
|
263
|
+
--seal-stale-text: var(--color-text-muted);
|
|
264
|
+
--seal-stale-bg: var(--color-surface-secondary);
|
|
265
|
+
|
|
251
266
|
/* Tag */
|
|
252
267
|
--tag-font: var(--type-label-font);
|
|
253
268
|
--tag-size: var(--type-label-size);
|
package/tokens/themes/ubp.css
CHANGED
|
@@ -138,6 +138,23 @@
|
|
|
138
138
|
/* Total color overrides: 22. Elevation: 2. Focus alias: 1.
|
|
139
139
|
Grand total: ~25 tokens. Typography/motion/spacing/grid unchanged.
|
|
140
140
|
--color-info and --color-info-subtle NOT overridden (inherit DS teal-blue). */
|
|
141
|
+
|
|
142
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
143
|
+
EVIDENCE SEAL — light-scheme text overrides (H1 Slice 2, uw#57)
|
|
144
|
+
--color-success (#16a34a) and --color-warning (#ca8a04) are
|
|
145
|
+
"large UI only" on the steel surface (3.15:1 and 2.83:1 resp.).
|
|
146
|
+
Seal badge text tokens use darker variants to clear WCAG AA (≥4.5:1)
|
|
147
|
+
on their respective subtle backgrounds.
|
|
148
|
+
|
|
149
|
+
Contrast on subtle backgrounds (AA = 4.5:1 for small badge text):
|
|
150
|
+
measured #256f20 (green-700) on #f0fdf4 (success-subtle): ~5.46:1 [AA]
|
|
151
|
+
projected #9e6308 (amber-700) on #fffbeb (warning-subtle): ~4.79:1 [AA]
|
|
152
|
+
--seal-inferred-text inherits --color-info (#2e63a3): 5.66:1 on
|
|
153
|
+
info-subtle (#f0f5fa) — no override needed.
|
|
154
|
+
--seal-stale-text inherits --color-text-muted (≥3.0:1 non-essential).
|
|
155
|
+
═══════════════════════════════════════════════════════════════ */
|
|
156
|
+
--seal-measured-text: #256f20; /* green-700 → 5.46:1 on success-subtle [AA] */
|
|
157
|
+
--seal-projected-text: #9e6308; /* amber-700 → 4.79:1 on warning-subtle [AA] */
|
|
141
158
|
}
|
|
142
159
|
|
|
143
160
|
/* Desktop density: compact button heights for toolbar/sidebar layouts.
|
|
@@ -222,3 +239,27 @@
|
|
|
222
239
|
/* Focus ring: accent blue, visible on all dark surfaces */
|
|
223
240
|
--focus-ring-color: var(--color-accent);
|
|
224
241
|
}
|
|
242
|
+
|
|
243
|
+
[data-theme="ubp"][data-scheme="dark"] {
|
|
244
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
245
|
+
EVIDENCE SEAL — dark-scheme overrides (H1 Slice 2, uw#57)
|
|
246
|
+
Text tokens lifted to 400-series for WCAG AA/AAA on steel-900
|
|
247
|
+
(#0b182a). Background tokens use color-mix() and are NOT
|
|
248
|
+
verified by the automated contrast suite (see ubp-theme-contrast
|
|
249
|
+
coverage-gap note) — visual QA is the complement.
|
|
250
|
+
|
|
251
|
+
Contrast on steel-900 (#0b182a, lum ≈ 0.0082):
|
|
252
|
+
measured #4ade80 (green-400) lum ≈ 0.403 → ~7.76:1 [AAA]
|
|
253
|
+
inferred #60a5fa (blue-400) lum ≈ 0.386 → ~7.49:1 [AAA]
|
|
254
|
+
projected #fbbf24 (amber-400) lum ≈ 0.598 → ~11.14:1 [AAA]
|
|
255
|
+
stale inherits --seal-stale-text: var(--color-text-muted) = #60758a (3.74:1)
|
|
256
|
+
═══════════════════════════════════════════════════════════════ */
|
|
257
|
+
--seal-measured-text: #4ade80;
|
|
258
|
+
--seal-measured-bg: color-mix(in srgb, #4ade80 12%, #0b182a);
|
|
259
|
+
--seal-inferred-text: #60a5fa;
|
|
260
|
+
--seal-inferred-bg: color-mix(in srgb, #60a5fa 12%, #0b182a);
|
|
261
|
+
--seal-projected-text: #fbbf24;
|
|
262
|
+
--seal-projected-bg: color-mix(in srgb, #fbbf24 12%, #0b182a);
|
|
263
|
+
/* stale: inherits --seal-stale-text (--color-text-muted = #60758a, 3.74:1 non-essential)
|
|
264
|
+
and --seal-stale-bg (--color-surface-secondary = #1a2a3a) */
|
|
265
|
+
}
|