ionbase-ui 0.22.0 → 0.25.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.
Files changed (56) hide show
  1. package/dist/components/AgentActivity.d.ts +41 -0
  2. package/dist/components/AgentActivity.d.ts.map +1 -0
  3. package/dist/components/AgentActivity.js +84 -0
  4. package/dist/components/AgentActivity.js.map +1 -0
  5. package/dist/components/AgentStop.d.ts +58 -0
  6. package/dist/components/AgentStop.d.ts.map +1 -0
  7. package/dist/components/AgentStop.js +82 -0
  8. package/dist/components/AgentStop.js.map +1 -0
  9. package/dist/components/ApprovalGate.d.ts +72 -0
  10. package/dist/components/ApprovalGate.d.ts.map +1 -0
  11. package/dist/components/ApprovalGate.js +68 -0
  12. package/dist/components/ApprovalGate.js.map +1 -0
  13. package/dist/components/Citation.d.ts +51 -0
  14. package/dist/components/Citation.d.ts.map +1 -0
  15. package/dist/components/Citation.js +41 -0
  16. package/dist/components/Citation.js.map +1 -0
  17. package/dist/components/ConfidenceIndicator.d.ts +37 -0
  18. package/dist/components/ConfidenceIndicator.d.ts.map +1 -0
  19. package/dist/components/ConfidenceIndicator.js +41 -0
  20. package/dist/components/ConfidenceIndicator.js.map +1 -0
  21. package/dist/components/StreamingText.d.ts +47 -0
  22. package/dist/components/StreamingText.d.ts.map +1 -0
  23. package/dist/components/StreamingText.js +44 -0
  24. package/dist/components/StreamingText.js.map +1 -0
  25. package/dist/components/index.d.ts +12 -0
  26. package/dist/components/index.d.ts.map +1 -1
  27. package/dist/components/index.js +6 -0
  28. package/dist/components/index.js.map +1 -1
  29. package/dist/figma-descriptions.json +147 -0
  30. package/dist/figma-map.json +3 -1
  31. package/dist/meta/AgentActivity.json +103 -0
  32. package/dist/meta/AgentActivityStep.json +117 -0
  33. package/dist/meta/AgentStop.json +164 -0
  34. package/dist/meta/ApprovalGate.json +255 -0
  35. package/dist/meta/Avatar.json +2 -2
  36. package/dist/meta/AvatarGroup.json +2 -2
  37. package/dist/meta/Citation.json +101 -0
  38. package/dist/meta/CitationList.json +94 -0
  39. package/dist/meta/CitationListItem.json +88 -0
  40. package/dist/meta/ConfidenceIndicator.json +115 -0
  41. package/dist/meta/Input.json +2 -2
  42. package/dist/meta/PhoneInput.json +38 -38
  43. package/dist/meta/StreamingText.json +99 -0
  44. package/dist/meta/components.json +2125 -989
  45. package/dist/meta/contrast.json +1086 -32
  46. package/dist/meta/index.json +100 -5
  47. package/dist/meta/patterns/index.json +1 -1
  48. package/dist/styles/agent-activity.css +95 -0
  49. package/dist/styles/agent-stop.css +109 -0
  50. package/dist/styles/approval-gate.css +181 -0
  51. package/dist/styles/citation.css +93 -0
  52. package/dist/styles/confidence-indicator.css +97 -0
  53. package/dist/styles/index.css +34 -0
  54. package/dist/styles/streaming-text.css +56 -0
  55. package/llms.txt +2 -2
  56. package/package.json +4 -3
@@ -1,11 +1,46 @@
1
1
  {
2
2
  "package": "ionbase-ui",
3
- "version": "0.22.0",
3
+ "version": "0.25.0",
4
4
  "usage": "Pick a component here, then read dist/meta/<Name>.json for its full contract.",
5
5
  "hooks": [
6
6
  "useToast"
7
7
  ],
8
8
  "components": {
9
+ "AgentActivity": {
10
+ "summary": "An ordered, plain-language log of what the agent is doing. Status is text and shape, never colour alone.",
11
+ "status": "stable",
12
+ "variants": {},
13
+ "detail": "dist/meta/AgentActivity.json",
14
+ "hasIntent": true
15
+ },
16
+ "AgentActivityStep": {
17
+ "summary": "One step in an activity log: what happened, in the user's language, with a status carried by shape and text.",
18
+ "status": "stable",
19
+ "variants": {
20
+ "status": [
21
+ "pending",
22
+ "active",
23
+ "done",
24
+ "failed",
25
+ "skipped"
26
+ ]
27
+ },
28
+ "detail": "dist/meta/AgentActivityStep.json",
29
+ "hasIntent": true
30
+ },
31
+ "AgentStop": {
32
+ "summary": "The always-visible way to end a running agent. Not optional, and not behind a menu.",
33
+ "status": "stable",
34
+ "variants": {
35
+ "size": [
36
+ "sm",
37
+ "md",
38
+ "lg"
39
+ ]
40
+ },
41
+ "detail": "dist/meta/AgentStop.json",
42
+ "hasIntent": true
43
+ },
9
44
  "Alert": {
10
45
  "summary": "A persistent, in-page status message. Intent picks the ARIA role for you.",
11
46
  "status": "stable",
@@ -30,15 +65,34 @@
30
65
  "detail": "dist/meta/Alert.json",
31
66
  "hasIntent": true
32
67
  },
68
+ "ApprovalGate": {
69
+ "summary": "A proposed action held until a person approves, rejects or amends it. It renders the decision; it does not enforce it.",
70
+ "status": "stable",
71
+ "variants": {
72
+ "risk": [
73
+ "low",
74
+ "medium",
75
+ "high"
76
+ ],
77
+ "status": [
78
+ "pending",
79
+ "approved",
80
+ "rejected",
81
+ "expired"
82
+ ]
83
+ },
84
+ "detail": "dist/meta/ApprovalGate.json",
85
+ "hasIntent": true
86
+ },
33
87
  "Avatar": {
34
88
  "summary": "A person or entity marker. Content is chosen by precedence — image, then initials, then icon — not by a type prop.",
35
89
  "status": "stable",
36
90
  "variants": {
37
91
  "size": [
38
- "mini",
39
92
  "sm",
40
93
  "md",
41
- "lg"
94
+ "lg",
95
+ "mini"
42
96
  ],
43
97
  "shape": [
44
98
  "circle",
@@ -53,10 +107,10 @@
53
107
  "status": "stable",
54
108
  "variants": {
55
109
  "size": [
56
- "mini",
57
110
  "sm",
58
111
  "md",
59
- "lg"
112
+ "lg",
113
+ "mini"
60
114
  ],
61
115
  "shape": [
62
116
  "circle",
@@ -123,6 +177,40 @@
123
177
  "detail": "dist/meta/Checkbox.json",
124
178
  "hasIntent": true
125
179
  },
180
+ "Citation": {
181
+ "summary": "The inline source marker. The visible number is for the eye; the accessible name is the sentence.",
182
+ "status": "stable",
183
+ "variants": {},
184
+ "detail": "dist/meta/Citation.json",
185
+ "hasIntent": true
186
+ },
187
+ "CitationList": {
188
+ "summary": "The footer list of sources, as an ordered list so the count and order are structural.",
189
+ "status": "stable",
190
+ "variants": {},
191
+ "detail": "dist/meta/CitationList.json",
192
+ "hasIntent": true
193
+ },
194
+ "CitationListItem": {
195
+ "summary": "One source row: its number, what it is, and the passage it supports.",
196
+ "status": "stable",
197
+ "variants": {},
198
+ "detail": "dist/meta/CitationListItem.json",
199
+ "hasIntent": true
200
+ },
201
+ "ConfidenceIndicator": {
202
+ "summary": "How much to trust the thing next to it — three levels with a required basis, never a percentage.",
203
+ "status": "stable",
204
+ "variants": {
205
+ "level": [
206
+ "low",
207
+ "medium",
208
+ "high"
209
+ ]
210
+ },
211
+ "detail": "dist/meta/ConfidenceIndicator.json",
212
+ "hasIntent": true
213
+ },
126
214
  "Divider": {
127
215
  "summary": "A semantic thematic break, rendered as a real <hr>.",
128
216
  "status": "stable",
@@ -357,6 +445,13 @@
357
445
  "detail": "dist/meta/Select.json",
358
446
  "hasIntent": true
359
447
  },
448
+ "StreamingText": {
449
+ "summary": "Model output arriving a token at a time, in a container that does not move. Deliberately not a live region.",
450
+ "status": "stable",
451
+ "variants": {},
452
+ "detail": "dist/meta/StreamingText.json",
453
+ "hasIntent": true
454
+ },
360
455
  "TabItem": {
361
456
  "summary": "One tab and its panel. Re-exported from react-stately's collection Item — `title` is the tab label, children are the panel.",
362
457
  "status": "stable",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "ionbase-ui",
3
- "version": "0.22.0",
3
+ "version": "0.25.0",
4
4
  "usage": "A pattern is a documented composition of components, not a component. Read one here, then read the contracts of what it composes.",
5
5
  "patterns": {
6
6
  "DataTable": {
@@ -0,0 +1,95 @@
1
+ /*
2
+ * AgentActivity
3
+ *
4
+ * NOT DRAWN IN FIGMA. The rail-and-body layout is Alert's, one step down in
5
+ * scale; colours are the status roles used everywhere else in the system.
6
+ */
7
+ .ion-agent-activity {
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: var(--spacing-8);
11
+ margin: 0;
12
+ padding: 0;
13
+ list-style: none;
14
+ font-family: var(--font-family-sans);
15
+ font-size: var(--type-body-sm);
16
+ line-height: var(--type-body-sm-line-height);
17
+ }
18
+
19
+ .ion-agent-activity__step {
20
+ display: flex;
21
+ align-items: flex-start;
22
+ gap: var(--spacing-8);
23
+ color: var(--text-secondary);
24
+ }
25
+
26
+ .ion-agent-activity__glyph {
27
+ display: inline-flex;
28
+ flex: none;
29
+ width: var(--icon-size-sm);
30
+ height: var(--icon-size-sm);
31
+
32
+ /* Optical alignment to the first line of the label, not to the box. */
33
+ margin-top: var(--spacing-2);
34
+ color: var(--icon-tertiary);
35
+ }
36
+
37
+ .ion-agent-activity__glyph svg {
38
+ width: 100%;
39
+ height: 100%;
40
+ }
41
+
42
+ .ion-agent-activity__body {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: var(--spacing-2);
46
+ min-width: 0;
47
+ }
48
+
49
+ .ion-agent-activity__detail {
50
+ color: var(--text-tertiary);
51
+ font-size: var(--type-caption);
52
+ line-height: var(--type-caption-line-height);
53
+ }
54
+
55
+ /*
56
+ * Status colour is the second signal, never the only one: each status also has
57
+ * a differently SHAPED glyph, and its name in visually hidden text. A row of
58
+ * dots that differ only in hue fails WCAG 1.4.1 outright.
59
+ */
60
+ .ion-agent-activity__step--active {
61
+ color: var(--text-default);
62
+ }
63
+
64
+ .ion-agent-activity__step--active .ion-agent-activity__glyph {
65
+ color: var(--icon-primary);
66
+ animation: ion-agent-activity-spin 1.2s linear infinite;
67
+ }
68
+
69
+ .ion-agent-activity__step--done .ion-agent-activity__glyph {
70
+ color: var(--icon-success);
71
+ }
72
+
73
+ .ion-agent-activity__step--failed {
74
+ color: var(--text-error);
75
+ }
76
+
77
+ .ion-agent-activity__step--failed .ion-agent-activity__glyph {
78
+ color: var(--icon-error);
79
+ }
80
+
81
+ .ion-agent-activity__step--skipped {
82
+ color: var(--text-tertiary);
83
+ }
84
+
85
+ @keyframes ion-agent-activity-spin {
86
+ to {
87
+ transform: rotate(360deg);
88
+ }
89
+ }
90
+
91
+ @media (prefers-reduced-motion: reduce) {
92
+ .ion-agent-activity__step--active .ion-agent-activity__glyph {
93
+ animation: none;
94
+ }
95
+ }
@@ -0,0 +1,109 @@
1
+ /*
2
+ * AgentStop
3
+ *
4
+ * NOT DRAWN IN FIGMA. This component has no Figma counterpart yet, so the
5
+ * measurements are borrowed rather than invented: the size ramp, radius, focus
6
+ * ring and icon sizing are Button's, read from the same tokens. When Figma
7
+ * draws it, this file should need values changed, not structure.
8
+ *
9
+ * DELIBERATELY NOT `surface/error`. Stopping a run is normal and expected, and
10
+ * you can start it again. Colouring it like a delete teaches hesitation about
11
+ * the one control that must never be hesitated over. It reads as a firm
12
+ * neutral: strong border, plain surface, error colour only on hover, where it
13
+ * confirms rather than warns.
14
+ */
15
+ .ion-agent-stop {
16
+ display: inline-flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ gap: var(--spacing-8);
20
+ min-height: var(--spacing-40);
21
+ padding: var(--spacing-8) var(--spacing-16);
22
+ border: var(--border-width-default) solid var(--border-strong);
23
+ border-radius: var(--radius-md);
24
+ background-color: var(--surface-default);
25
+ color: var(--text-default);
26
+ font-family: var(--font-family-sans);
27
+ font-size: var(--type-body);
28
+ line-height: var(--type-body-line-height);
29
+ font-weight: var(--font-weight-medium);
30
+ cursor: pointer;
31
+ transition:
32
+ background-color var(--ion-duration-base) var(--ion-ease-out),
33
+ border-color var(--ion-duration-base) var(--ion-ease-out),
34
+ color var(--ion-duration-base) var(--ion-ease-out);
35
+ }
36
+
37
+ .ion-agent-stop:focus-visible {
38
+ outline: var(--border-width-thick) solid var(--border-focus);
39
+ outline-offset: 2px;
40
+ }
41
+
42
+ /*
43
+ * Disabled ONLY while the stop is in flight, never as a resting state. A
44
+ * greyed-out stop button on a running agent is the failure this component
45
+ * exists to prevent, so the styling deliberately does not read as
46
+ * "unavailable" — it reads as "already asked".
47
+ *
48
+ * Declared before :hover so specificity reads non-decreasing; the :not(:disabled)
49
+ * on the hover rules is what keeps them from fighting.
50
+ */
51
+ .ion-agent-stop:disabled {
52
+ cursor: default;
53
+ background-color: var(--surface-muted);
54
+ border-color: var(--border-default);
55
+ color: var(--text-secondary);
56
+ }
57
+
58
+ .ion-agent-stop:hover:not(:disabled) {
59
+ background-color: var(--surface-error-subtle);
60
+ border-color: var(--border-error);
61
+ color: var(--text-error);
62
+ }
63
+
64
+ .ion-agent-stop:active:not(:disabled) {
65
+ background-color: var(--surface-error-subtle-hover);
66
+ }
67
+
68
+ .ion-agent-stop__glyph {
69
+ display: inline-flex;
70
+ flex: none;
71
+ width: var(--icon-size-sm);
72
+ height: var(--icon-size-sm);
73
+ }
74
+
75
+ .ion-agent-stop__glyph svg {
76
+ width: 100%;
77
+ height: 100%;
78
+ }
79
+
80
+ /* The square shrinks while stopping — the one motion cue that the press
81
+ * landed, on a control whose text also changed. */
82
+ .ion-agent-stop[data-stopping] .ion-agent-stop__glyph svg {
83
+ transform: scale(0.7);
84
+ transition: transform var(--ion-duration-base) var(--ion-ease-out);
85
+ }
86
+
87
+ .ion-agent-stop--sm {
88
+ min-height: var(--spacing-32);
89
+ padding: var(--spacing-4) var(--spacing-12);
90
+ font-size: var(--type-body-sm);
91
+ line-height: var(--type-body-sm-line-height);
92
+ }
93
+
94
+ .ion-agent-stop--sm .ion-agent-stop__glyph {
95
+ width: var(--icon-size-xs);
96
+ height: var(--icon-size-xs);
97
+ }
98
+
99
+ .ion-agent-stop--lg {
100
+ min-height: var(--spacing-48);
101
+ padding: var(--spacing-12) var(--spacing-20);
102
+ font-size: var(--type-body-lg);
103
+ line-height: var(--type-body-lg-line-height);
104
+ }
105
+
106
+ .ion-agent-stop--lg .ion-agent-stop__glyph {
107
+ width: var(--icon-size-md);
108
+ height: var(--icon-size-md);
109
+ }
@@ -0,0 +1,181 @@
1
+ /*
2
+ * ApprovalGate
3
+ *
4
+ * NOT DRAWN IN FIGMA. Structure is Alert's — icon rail, body, one variable per
5
+ * colour slot — so that when Figma draws it, this file needs values changed
6
+ * rather than rewriting. The action row borrows Button's ramp.
7
+ *
8
+ * RISK CHANGES EMPHASIS, NOT MEANING
9
+ *
10
+ * Three risk tiers reassign four variables, exactly the way Alert's intents do.
11
+ * `low` sits on the neutral surface, `medium` on information, `high` on
12
+ * warning — NOT on error. Error is for something that has gone wrong; a
13
+ * high-risk approval is working correctly, and it is asking. Reserving red for
14
+ * failure is what keeps red meaningful when a failure actually happens.
15
+ */
16
+ .ion-approval-gate {
17
+ --ion-approval-surface: var(--surface-information-subtle);
18
+ --ion-approval-border: var(--border-information);
19
+ --ion-approval-icon: var(--icon-information);
20
+ --ion-approval-accent: var(--text-information);
21
+
22
+ position: relative;
23
+ display: flex;
24
+ align-items: flex-start;
25
+ gap: var(--spacing-12);
26
+ padding: var(--spacing-16);
27
+ background-color: var(--ion-approval-surface);
28
+
29
+ /*
30
+ * A thicker leading edge, not a heavier box. A decision waiting on a person
31
+ * has to be findable at a glance down a long transcript, and a full heavy
32
+ * border would compete with every Alert on the page.
33
+ */
34
+ border: var(--border-width-default) solid var(--ion-approval-border);
35
+ border-inline-start-width: var(--border-width-thicker);
36
+ border-radius: var(--radius-md);
37
+ color: var(--text-default);
38
+ font-family: var(--font-family-sans);
39
+ font-size: var(--type-body);
40
+ line-height: var(--type-body-line-height);
41
+ }
42
+
43
+ .ion-approval-gate--low {
44
+ --ion-approval-surface: var(--surface-muted);
45
+ --ion-approval-border: var(--border-default);
46
+ --ion-approval-icon: var(--icon-secondary);
47
+ --ion-approval-accent: var(--text-secondary);
48
+ }
49
+
50
+ .ion-approval-gate--high {
51
+ --ion-approval-surface: var(--surface-warning-subtle);
52
+ --ion-approval-border: var(--border-warning-strong);
53
+ --ion-approval-icon: var(--icon-warning);
54
+ --ion-approval-accent: var(--text-warning);
55
+ }
56
+
57
+ /* Resolved: the decision is a record now, not a question. Drop the tint and
58
+ * keep the rule, so the row stays scannable without still looking pending. */
59
+ .ion-approval-gate--approved,
60
+ .ion-approval-gate--rejected,
61
+ .ion-approval-gate--expired {
62
+ --ion-approval-surface: var(--surface-default);
63
+ --ion-approval-border: var(--border-default);
64
+ --ion-approval-icon: var(--icon-tertiary);
65
+ }
66
+
67
+ .ion-approval-gate--approved {
68
+ --ion-approval-border: var(--border-success);
69
+ --ion-approval-icon: var(--icon-success);
70
+ --ion-approval-accent: var(--text-success);
71
+ }
72
+
73
+ .ion-approval-gate--rejected {
74
+ --ion-approval-accent: var(--text-secondary);
75
+ }
76
+
77
+ .ion-approval-gate__icon {
78
+ display: inline-flex;
79
+ flex: none;
80
+ width: var(--icon-size-md);
81
+ height: var(--icon-size-md);
82
+ color: var(--ion-approval-icon);
83
+ }
84
+
85
+ .ion-approval-gate__icon svg {
86
+ width: 100%;
87
+ height: 100%;
88
+ }
89
+
90
+ .ion-approval-gate__body {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: var(--spacing-8);
94
+ min-width: 0;
95
+ flex: 1;
96
+ }
97
+
98
+ .ion-approval-gate__title {
99
+ margin: 0;
100
+ font-weight: var(--font-weight-semibold);
101
+ color: var(--text-default);
102
+ }
103
+
104
+ /* Focusable only programmatically, so it takes a ring but no tab stop. */
105
+ .ion-approval-gate__title:focus-visible {
106
+ outline: var(--border-width-thick) solid var(--border-focus);
107
+ outline-offset: 2px;
108
+ border-radius: var(--radius-xs);
109
+ }
110
+
111
+ .ion-approval-gate__detail {
112
+ margin: 0;
113
+ color: var(--text-secondary);
114
+ font-size: var(--type-body-sm);
115
+ line-height: var(--type-body-sm-line-height);
116
+ }
117
+
118
+ .ion-approval-gate__actions {
119
+ display: flex;
120
+ flex-wrap: wrap;
121
+ gap: var(--spacing-8);
122
+ margin-top: var(--spacing-4);
123
+ }
124
+
125
+ .ion-approval-gate__action {
126
+ display: inline-flex;
127
+ align-items: center;
128
+ justify-content: center;
129
+ min-height: var(--spacing-32);
130
+ padding: var(--spacing-4) var(--spacing-12);
131
+ border: var(--border-width-default) solid var(--border-strong);
132
+ border-radius: var(--radius-sm);
133
+ background-color: var(--surface-default);
134
+ color: var(--text-default);
135
+ font-family: var(--font-family-sans);
136
+ font-size: var(--type-body-sm);
137
+ line-height: var(--type-body-sm-line-height);
138
+ font-weight: var(--font-weight-medium);
139
+ cursor: pointer;
140
+ transition:
141
+ background-color var(--ion-duration-base) var(--ion-ease-out),
142
+ border-color var(--ion-duration-base) var(--ion-ease-out);
143
+ }
144
+
145
+ .ion-approval-gate__action:focus-visible {
146
+ outline: var(--border-width-thick) solid var(--border-focus);
147
+ outline-offset: 2px;
148
+ }
149
+
150
+ /* Before :hover so specificity reads non-decreasing. */
151
+ .ion-approval-gate__action:disabled {
152
+ cursor: default;
153
+ background-color: var(--surface-disabled);
154
+ border-color: var(--border-disabled);
155
+ color: var(--text-disabled);
156
+ }
157
+
158
+ .ion-approval-gate__action:hover:not(:disabled) {
159
+ background-color: var(--surface-hover);
160
+ }
161
+
162
+ /*
163
+ * Approve is the only filled button, and it is filled in brand rather than
164
+ * success. Success would state an outcome before there is one.
165
+ */
166
+ .ion-approval-gate__action--approve {
167
+ background-color: var(--surface-primary);
168
+ border-color: var(--surface-primary);
169
+ color: var(--text-on-color);
170
+ }
171
+
172
+ .ion-approval-gate__action--approve:hover:not(:disabled) {
173
+ background-color: var(--surface-primary-hover);
174
+ border-color: var(--surface-primary-hover);
175
+ }
176
+
177
+ .ion-approval-gate__resolution {
178
+ margin: 0;
179
+ font-weight: var(--font-weight-medium);
180
+ color: var(--ion-approval-accent);
181
+ }
@@ -0,0 +1,93 @@
1
+ /*
2
+ * Citation
3
+ *
4
+ * NOT DRAWN IN FIGMA. The inline marker borrows Badge's pill geometry at the
5
+ * smallest size; the footer list borrows the Table's row rhythm.
6
+ */
7
+ .ion-citation {
8
+ display: inline-flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ min-width: var(--spacing-16);
12
+ height: var(--spacing-16);
13
+ padding: 0 var(--spacing-4);
14
+
15
+ /* Sits with the text, not on it: superscript positioning would shift the
16
+ * line box and make a paragraph of citations ripple. */
17
+ margin-inline-start: var(--spacing-2);
18
+ border-radius: var(--radius-xs);
19
+ background-color: var(--surface-primary-subtle);
20
+ color: var(--text-link);
21
+ font-size: var(--type-caption);
22
+ line-height: var(--type-caption-line-height);
23
+ font-weight: var(--font-weight-medium);
24
+ text-decoration: none;
25
+ vertical-align: baseline;
26
+ }
27
+
28
+ a.ion-citation:hover {
29
+ background-color: var(--surface-primary-subtle-hover);
30
+ }
31
+
32
+ a.ion-citation:focus-visible {
33
+ outline: var(--border-width-thick) solid var(--border-focus);
34
+ outline-offset: 1px;
35
+ }
36
+
37
+ .ion-citation-list {
38
+ font-family: var(--font-family-sans);
39
+ font-size: var(--type-body-sm);
40
+ line-height: var(--type-body-sm-line-height);
41
+ }
42
+
43
+ .ion-citation-list__label {
44
+ margin: 0 0 var(--spacing-8);
45
+ color: var(--text-tertiary);
46
+ font-size: var(--type-caption);
47
+ line-height: var(--type-caption-line-height);
48
+ font-weight: var(--font-weight-semibold);
49
+ }
50
+
51
+ .ion-citation-list__items {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: var(--spacing-8);
55
+ margin: 0;
56
+ padding: 0;
57
+
58
+ /* The <ol> keeps its semantics and loses its markers: the visible number is
59
+ * the citation's own, which must match the inline marker exactly. */
60
+ list-style: none;
61
+ }
62
+
63
+ .ion-citation-list__item {
64
+ display: flex;
65
+ align-items: flex-start;
66
+ gap: var(--spacing-8);
67
+ }
68
+
69
+ .ion-citation-list__marker {
70
+ flex: none;
71
+ min-width: var(--spacing-16);
72
+ color: var(--text-tertiary);
73
+ font-size: var(--type-caption);
74
+ line-height: var(--type-body-sm-line-height);
75
+ text-align: end;
76
+ }
77
+
78
+ .ion-citation-list__body {
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: var(--spacing-2);
82
+ min-width: 0;
83
+ }
84
+
85
+ .ion-citation-list__source {
86
+ color: var(--text-link);
87
+ }
88
+
89
+ .ion-citation-list__passage {
90
+ color: var(--text-tertiary);
91
+ font-size: var(--type-caption);
92
+ line-height: var(--type-caption-line-height);
93
+ }