ionbase-ui 0.51.0 → 0.69.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/dist/components/Accordion.d.ts +54 -0
- package/dist/components/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion.js +68 -0
- package/dist/components/Accordion.js.map +1 -0
- package/dist/components/AgentActivity.d.ts +2 -1
- package/dist/components/AgentActivity.d.ts.map +1 -1
- package/dist/components/AgentActivity.js +4 -17
- package/dist/components/AgentActivity.js.map +1 -1
- package/dist/components/Breadcrumb.d.ts +45 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.js +31 -0
- package/dist/components/Breadcrumb.js.map +1 -0
- package/dist/components/Calendar.d.ts +20 -0
- package/dist/components/Calendar.d.ts.map +1 -0
- package/dist/components/Calendar.js +151 -0
- package/dist/components/Calendar.js.map +1 -0
- package/dist/components/CalendarPopover.d.ts +26 -0
- package/dist/components/CalendarPopover.d.ts.map +1 -0
- package/dist/components/CalendarPopover.js +45 -0
- package/dist/components/CalendarPopover.js.map +1 -0
- package/dist/components/Checkbox.d.ts +2 -1
- package/dist/components/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox.js +4 -2
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Combobox.d.ts +86 -0
- package/dist/components/Combobox.d.ts.map +1 -0
- package/dist/components/Combobox.js +261 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/DateField.d.ts +15 -0
- package/dist/components/DateField.d.ts.map +1 -0
- package/dist/components/DateField.js +74 -0
- package/dist/components/DateField.js.map +1 -0
- package/dist/components/DatePicker.d.ts +81 -0
- package/dist/components/DatePicker.d.ts.map +1 -0
- package/dist/components/DatePicker.js +102 -0
- package/dist/components/DatePicker.js.map +1 -0
- package/dist/components/DateRangePicker.d.ts +117 -0
- package/dist/components/DateRangePicker.d.ts.map +1 -0
- package/dist/components/DateRangePicker.js +152 -0
- package/dist/components/DateRangePicker.js.map +1 -0
- package/dist/components/Drawer.d.ts +52 -0
- package/dist/components/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer.js +66 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/FileUpload.d.ts +79 -0
- package/dist/components/FileUpload.d.ts.map +1 -0
- package/dist/components/FileUpload.js +218 -0
- package/dist/components/FileUpload.js.map +1 -0
- package/dist/components/Header.d.ts +10 -0
- package/dist/components/Header.d.ts.map +1 -1
- package/dist/components/Header.js +18 -3
- package/dist/components/Header.js.map +1 -1
- package/dist/components/NumberInput.d.ts +63 -0
- package/dist/components/NumberInput.d.ts.map +1 -0
- package/dist/components/NumberInput.js +102 -0
- package/dist/components/NumberInput.js.map +1 -0
- package/dist/components/ProgressBar.d.ts +46 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +52 -0
- package/dist/components/ProgressBar.js.map +1 -0
- package/dist/components/PromptInput.d.ts +89 -0
- package/dist/components/PromptInput.d.ts.map +1 -0
- package/dist/components/PromptInput.js +130 -0
- package/dist/components/PromptInput.js.map +1 -0
- package/dist/components/SegmentedControl.d.ts +63 -0
- package/dist/components/SegmentedControl.d.ts.map +1 -0
- package/dist/components/SegmentedControl.js +79 -0
- package/dist/components/SegmentedControl.js.map +1 -0
- package/dist/components/Select.d.ts +9 -2
- package/dist/components/Select.d.ts.map +1 -1
- package/dist/components/Select.js.map +1 -1
- package/dist/components/Sidebar.d.ts +109 -0
- package/dist/components/Sidebar.d.ts.map +1 -0
- package/dist/components/Sidebar.js +106 -0
- package/dist/components/Sidebar.js.map +1 -0
- package/dist/components/Skeleton.d.ts +39 -0
- package/dist/components/Skeleton.d.ts.map +1 -0
- package/dist/components/Skeleton.js +50 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/Spinner.d.ts +45 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +44 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/Stepper.d.ts +73 -0
- package/dist/components/Stepper.d.ts.map +1 -0
- package/dist/components/Stepper.js +82 -0
- package/dist/components/Stepper.js.map +1 -0
- package/dist/components/Toggle.d.ts +2 -1
- package/dist/components/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle.js +4 -2
- package/dist/components/Toggle.js.map +1 -1
- package/dist/components/ToolCall.d.ts +66 -0
- package/dist/components/ToolCall.d.ts.map +1 -0
- package/dist/components/ToolCall.js +78 -0
- package/dist/components/ToolCall.js.map +1 -0
- package/dist/components/agent-status.d.ts +14 -0
- package/dist/components/agent-status.d.ts.map +1 -0
- package/dist/components/agent-status.js +16 -0
- package/dist/components/agent-status.js.map +1 -0
- package/dist/components/index.d.ts +33 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +16 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/iso-date.d.ts +53 -0
- package/dist/components/iso-date.d.ts.map +1 -0
- package/dist/components/iso-date.js +102 -0
- package/dist/components/iso-date.js.map +1 -0
- package/dist/components/resolve-selection.d.ts +36 -0
- package/dist/components/resolve-selection.d.ts.map +1 -0
- package/dist/components/resolve-selection.js +14 -0
- package/dist/components/resolve-selection.js.map +1 -0
- package/dist/figma-descriptions.json +255 -145
- package/dist/figma-map.json +3205 -1860
- package/dist/meta/Accordion.json +123 -0
- package/dist/meta/AccordionItem.json +90 -0
- package/dist/meta/AgentActivityStep.json +2 -2
- package/dist/meta/Breadcrumb.json +87 -0
- package/dist/meta/BreadcrumbItem.json +78 -0
- package/dist/meta/Checkbox.json +18 -1
- package/dist/meta/Combobox.json +265 -0
- package/dist/meta/DatePicker.json +270 -0
- package/dist/meta/DateRangePicker.json +284 -0
- package/dist/meta/Drawer.json +234 -0
- package/dist/meta/EmptyState.json +1 -1
- package/dist/meta/FileUpload.json +246 -0
- package/dist/meta/Header.json +32 -2
- package/dist/meta/NumberInput.json +407 -0
- package/dist/meta/PhoneInput.json +39 -39
- package/dist/meta/Popover.json +3 -3
- package/dist/meta/ProgressBar.json +166 -0
- package/dist/meta/PromptInput.json +212 -0
- package/dist/meta/SegmentedControl.json +217 -0
- package/dist/meta/SegmentedControlItem.json +122 -0
- package/dist/meta/Select.json +4 -3
- package/dist/meta/Sidebar.json +150 -0
- package/dist/meta/SidebarItem.json +184 -0
- package/dist/meta/SidebarSection.json +131 -0
- package/dist/meta/Skeleton.json +120 -0
- package/dist/meta/Spinner.json +123 -0
- package/dist/meta/Stepper.json +135 -0
- package/dist/meta/StepperStep.json +148 -0
- package/dist/meta/Tabs.json +5 -0
- package/dist/meta/Toggle.json +18 -1
- package/dist/meta/ToolCall.json +192 -0
- package/dist/meta/Tooltip.json +3 -3
- package/dist/meta/components.json +6759 -2705
- package/dist/meta/contrast.json +11075 -3098
- package/dist/meta/index.json +266 -8
- package/dist/meta/patterns/AgentRun.json +27 -0
- package/dist/meta/patterns/Form.json +9 -0
- package/dist/meta/patterns/PageShell.json +43 -3
- package/dist/meta/patterns/Wizard.json +20 -1
- package/dist/meta/patterns/index.json +10 -1
- package/dist/styles/accordion.css +102 -0
- package/dist/styles/breadcrumb.css +72 -0
- package/dist/styles/combobox.css +154 -0
- package/dist/styles/date-picker.css +420 -0
- package/dist/styles/date-range-picker.css +280 -0
- package/dist/styles/drawer.css +181 -0
- package/dist/styles/empty-state.css +16 -4
- package/dist/styles/file-upload.css +236 -0
- package/dist/styles/header.css +26 -0
- package/dist/styles/index.css +23 -0
- package/dist/styles/number-input.css +83 -0
- package/dist/styles/progress-bar.css +107 -0
- package/dist/styles/prompt-input.css +127 -0
- package/dist/styles/segmented-control.css +182 -0
- package/dist/styles/sidebar.css +307 -0
- package/dist/styles/skeleton.css +76 -0
- package/dist/styles/spinner.css +70 -0
- package/dist/styles/stepper.css +250 -0
- package/dist/styles/tool-call.css +202 -0
- package/llms.txt +2 -2
- package/package.json +2 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* PromptInput
|
|
3
|
+
*
|
|
4
|
+
* Drawn in Figma as `Prompt Input` (1393:387), from this file, composing Icon
|
|
5
|
+
* Button and Agent Stop instances rather than redrawing them. The box is Textarea's — surface, border, hover and focus
|
|
6
|
+
* roles unchanged — one radius rung larger, because it holds a toolbar and
|
|
7
|
+
* reads as a surface rather than a field. The send control is Button `sm`
|
|
8
|
+
* made square; the stop control is AgentStop `sm`, unmodified.
|
|
9
|
+
*
|
|
10
|
+
* The border and focus live on the BOX, not the textarea. The box is what
|
|
11
|
+
* looks like the field, so it is what shows focus; a ring around only the text
|
|
12
|
+
* area inside it would draw a field within a field.
|
|
13
|
+
*
|
|
14
|
+
* States are BEM modifiers and `:focus-within`, never `[data-*]`, so the
|
|
15
|
+
* contrast gate measures them — see stepper.css for what happens otherwise.
|
|
16
|
+
*/
|
|
17
|
+
.ion-prompt-input {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--spacing-8);
|
|
21
|
+
padding: var(--spacing-8);
|
|
22
|
+
border: var(--border-width-default) solid var(--border-default);
|
|
23
|
+
border-radius: var(--radius-lg);
|
|
24
|
+
background-color: var(--surface-default);
|
|
25
|
+
color: var(--text-default);
|
|
26
|
+
font-family: var(--font-family-sans);
|
|
27
|
+
cursor: text;
|
|
28
|
+
transition: border-color var(--ion-duration-base) var(--ion-ease-out);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@media (hover: hover) {
|
|
32
|
+
.ion-prompt-input:hover:not(.ion-prompt-input--disabled) {
|
|
33
|
+
border-color: var(--border-strong);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Same specificity as the hover rule above and declared after it, so focus
|
|
39
|
+
* wins while the pointer is still over the box — which it always is right
|
|
40
|
+
* after a click into the field.
|
|
41
|
+
*/
|
|
42
|
+
.ion-prompt-input:focus-within:not(.ion-prompt-input--disabled) {
|
|
43
|
+
border-color: var(--border-focus);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.ion-prompt-input__attachments {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-wrap: wrap;
|
|
49
|
+
gap: var(--spacing-8);
|
|
50
|
+
padding-inline: var(--spacing-4);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.ion-prompt-input__field {
|
|
54
|
+
display: block;
|
|
55
|
+
width: 100%;
|
|
56
|
+
margin: 0;
|
|
57
|
+
|
|
58
|
+
/* Lines the text up with the actions' left edge below it. */
|
|
59
|
+
padding: var(--spacing-4);
|
|
60
|
+
border: 0;
|
|
61
|
+
outline: none;
|
|
62
|
+
background-color: transparent;
|
|
63
|
+
color: var(--text-default);
|
|
64
|
+
font: inherit;
|
|
65
|
+
font-size: var(--type-body);
|
|
66
|
+
line-height: var(--type-body-line-height);
|
|
67
|
+
|
|
68
|
+
/* Height is set from the content, up to maxRows; a grip would fight it. */
|
|
69
|
+
resize: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* text/tertiary, as Textarea — text/placeholder was retired for 2.38:1. */
|
|
73
|
+
.ion-prompt-input__field::placeholder {
|
|
74
|
+
color: var(--text-tertiary);
|
|
75
|
+
opacity: 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.ion-prompt-input__toolbar {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
justify-content: space-between;
|
|
82
|
+
gap: var(--spacing-8);
|
|
83
|
+
cursor: default;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ion-prompt-input__actions {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
gap: var(--spacing-4);
|
|
90
|
+
min-width: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* Square, at Button's own `sm` height. Two classes, so it outranks
|
|
95
|
+
* `.ion-button--sm` without depending on which stylesheet loads first.
|
|
96
|
+
*/
|
|
97
|
+
.ion-prompt-input .ion-prompt-input__send {
|
|
98
|
+
--ion-button-padding-x: 0;
|
|
99
|
+
|
|
100
|
+
flex: none;
|
|
101
|
+
width: var(--spacing-32);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.ion-prompt-input__stop {
|
|
105
|
+
flex: none;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.ion-prompt-input--disabled {
|
|
109
|
+
border-color: var(--border-disabled);
|
|
110
|
+
background-color: var(--surface-disabled);
|
|
111
|
+
cursor: not-allowed;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.ion-prompt-input--disabled .ion-prompt-input__field {
|
|
115
|
+
color: var(--text-disabled);
|
|
116
|
+
cursor: not-allowed;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.ion-prompt-input--disabled .ion-prompt-input__field::placeholder {
|
|
120
|
+
color: var(--text-disabled);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@media (prefers-reduced-motion: reduce) {
|
|
124
|
+
.ion-prompt-input {
|
|
125
|
+
transition: none;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SegmentedControl
|
|
3
|
+
*
|
|
4
|
+
* Drawn in Figma as `Segmented Control` (1390:435) and `Segmented Control
|
|
5
|
+
* Item` (1390:332), from this file. It is Tabs' pill track, token for token: the muted
|
|
6
|
+
* track, the 2px gap, the default-surface selected segment, the page-surface
|
|
7
|
+
* selected hover, and the overall height ladder (32/40/48, 12/16/20 padding). Two
|
|
8
|
+
* components that look identical must not drift into looking almost
|
|
9
|
+
* identical, so if tabs.css changes its pill, this changes with it.
|
|
10
|
+
*
|
|
11
|
+
* Two deliberate differences from the pill track. It is `inline-flex` in every
|
|
12
|
+
* orientation, because a radiogroup has none. And the track radius is `lg`
|
|
13
|
+
* rather than `full`, so `--full-width` does not stretch a stadium shape across
|
|
14
|
+
* a card.
|
|
15
|
+
*
|
|
16
|
+
* States are BEM modifiers (`--selected`, `--disabled`, `--focus-visible`),
|
|
17
|
+
* never `[data-*]`, and the track surface is on the block, so the contrast gate
|
|
18
|
+
* measures the translucent hover against it — see stepper.css and
|
|
19
|
+
* number-input.css for the two ways that goes wrong.
|
|
20
|
+
*/
|
|
21
|
+
.ion-segmented-control {
|
|
22
|
+
--ion-segmented-item-height: var(--spacing-32);
|
|
23
|
+
--ion-segmented-item-padding-x: var(--spacing-16);
|
|
24
|
+
--ion-segmented-item-gap: var(--spacing-8);
|
|
25
|
+
--ion-segmented-icon-size: var(--icon-size-md);
|
|
26
|
+
--ion-segmented-font-size: var(--type-body);
|
|
27
|
+
--ion-segmented-line-height: var(--type-body-line-height);
|
|
28
|
+
|
|
29
|
+
display: inline-flex;
|
|
30
|
+
gap: var(--spacing-2);
|
|
31
|
+
padding: var(--spacing-4);
|
|
32
|
+
border-radius: var(--radius-lg);
|
|
33
|
+
background-color: var(--surface-muted);
|
|
34
|
+
color: var(--text-tertiary);
|
|
35
|
+
font-family: var(--font-family-sans);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Item heights are the pill's 40/32/48 less the track's 4px inset on each side. */
|
|
39
|
+
.ion-segmented-control--sm {
|
|
40
|
+
--ion-segmented-item-height: var(--spacing-24);
|
|
41
|
+
--ion-segmented-item-padding-x: var(--spacing-12);
|
|
42
|
+
--ion-segmented-item-gap: var(--spacing-6);
|
|
43
|
+
--ion-segmented-icon-size: var(--icon-size-sm);
|
|
44
|
+
--ion-segmented-font-size: var(--type-body-sm);
|
|
45
|
+
--ion-segmented-line-height: var(--type-body-sm-line-height);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.ion-segmented-control--lg {
|
|
49
|
+
--ion-segmented-item-height: var(--spacing-40);
|
|
50
|
+
--ion-segmented-item-padding-x: var(--spacing-20);
|
|
51
|
+
--ion-segmented-font-size: var(--type-body-lg);
|
|
52
|
+
--ion-segmented-line-height: var(--type-body-lg-line-height);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.ion-segmented-control--full-width {
|
|
56
|
+
display: flex;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.ion-segmented-control__item {
|
|
60
|
+
display: inline-flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
gap: var(--ion-segmented-item-gap);
|
|
64
|
+
min-height: var(--ion-segmented-item-height);
|
|
65
|
+
padding-inline: var(--ion-segmented-item-padding-x);
|
|
66
|
+
border-radius: var(--radius-md);
|
|
67
|
+
color: var(--text-tertiary);
|
|
68
|
+
font-size: var(--ion-segmented-font-size);
|
|
69
|
+
line-height: var(--ion-segmented-line-height);
|
|
70
|
+
font-weight: var(--font-weight-medium);
|
|
71
|
+
white-space: nowrap;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
user-select: none;
|
|
74
|
+
transition:
|
|
75
|
+
color var(--ion-duration-base) var(--ion-ease-out),
|
|
76
|
+
background-color var(--ion-duration-base) var(--ion-ease-out);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.ion-segmented-control--full-width .ion-segmented-control__item {
|
|
80
|
+
flex: 1 1 0;
|
|
81
|
+
min-width: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.ion-segmented-control__icon {
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
flex: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.ion-segmented-control__icon svg {
|
|
90
|
+
width: var(--ion-segmented-icon-size);
|
|
91
|
+
height: var(--ion-segmented-icon-size);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.ion-segmented-control__label {
|
|
95
|
+
overflow: hidden;
|
|
96
|
+
text-overflow: ellipsis;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* `.ion-field` is a column that stretches its children; a labelled control
|
|
100
|
+
* keeps its own width unless it was asked to fill. */
|
|
101
|
+
.ion-segmented-control-field {
|
|
102
|
+
align-items: flex-start;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.ion-segmented-control-field--full-width {
|
|
106
|
+
align-items: stretch;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* State order is specificity, not `:not()` lists. The contrast gate splits a
|
|
111
|
+
* selector at every comma and reads `--disabled` inside `:not(...)` as the
|
|
112
|
+
* disabled state, so a hover rule guarded with `:not(--selected, --disabled)`
|
|
113
|
+
* was measured as disabled text on a hover surface — two failures for a
|
|
114
|
+
* combination that never renders. Plain rules, ordered, say the same thing
|
|
115
|
+
* and are read correctly:
|
|
116
|
+
*
|
|
117
|
+
* hover (0,2,0) -> selected (0,1,0) -> selected hover (0,2,0)
|
|
118
|
+
* -> disabled (0,2,0) -> disabled selected (0,3,0)
|
|
119
|
+
*/
|
|
120
|
+
@media (hover: hover) {
|
|
121
|
+
.ion-segmented-control__item:hover {
|
|
122
|
+
background-color: var(--surface-hover);
|
|
123
|
+
color: var(--text-default);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.ion-segmented-control__item--selected {
|
|
128
|
+
background-color: var(--surface-default);
|
|
129
|
+
color: var(--text-default);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@media (hover: hover) {
|
|
133
|
+
.ion-segmented-control__item--selected:hover {
|
|
134
|
+
background-color: var(--surface-page);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/*
|
|
139
|
+
* Focus lands on the hidden radio, so the ring is drawn on the segment around
|
|
140
|
+
* it. Tabs' ring: 2px `ring/focus`, offset by its own width.
|
|
141
|
+
*/
|
|
142
|
+
.ion-segmented-control__item--focus-visible {
|
|
143
|
+
outline: var(--border-width-thick) solid var(--ring-focus);
|
|
144
|
+
outline-offset: var(--border-width-thick);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/*
|
|
148
|
+
* Two classes, so it outranks hover: a disabled segment does not react.
|
|
149
|
+
*
|
|
150
|
+
* The track's own surface rather than `transparent` — identical on screen,
|
|
151
|
+
* because the segment sits on the track. It is explicit for the contrast gate:
|
|
152
|
+
* with `transparent` the surface is inherited from the block, and the gate
|
|
153
|
+
* treats a single modifier that takes its surface from the block, while a
|
|
154
|
+
* compound of it (`--selected` + `--disabled`) supplies its own, as a state
|
|
155
|
+
* that cannot render — and drops it without counting a skip.
|
|
156
|
+
*/
|
|
157
|
+
.ion-segmented-control .ion-segmented-control__item--disabled {
|
|
158
|
+
background-color: var(--surface-muted);
|
|
159
|
+
color: var(--text-disabled);
|
|
160
|
+
cursor: not-allowed;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/*
|
|
164
|
+
* A disabled SELECTED segment must still show which one is selected — a
|
|
165
|
+
* disabled control still has a value. Only the text dims.
|
|
166
|
+
*/
|
|
167
|
+
.ion-segmented-control
|
|
168
|
+
.ion-segmented-control__item--selected.ion-segmented-control__item--disabled {
|
|
169
|
+
background-color: var(--surface-default);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@media (forced-colors: active) {
|
|
173
|
+
.ion-segmented-control__item--selected {
|
|
174
|
+
outline: var(--border-width-thick) solid;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@media (prefers-reduced-motion: reduce) {
|
|
179
|
+
.ion-segmented-control__item {
|
|
180
|
+
transition: none;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Sidebar
|
|
3
|
+
*
|
|
4
|
+
* Drawn in Figma as `Sidebar` (1396:502), `Sidebar Section` (1396:501) and
|
|
5
|
+
* `Sidebar Item` (1396:412), from this file. Rows take Menu Item's rhythm — 32px, 8px inset, `md`
|
|
6
|
+
* radius — because a sidebar row and a menu row are the same density of
|
|
7
|
+
* choice. The column is `surface/default` against the page's `surface/page`,
|
|
8
|
+
* the same step a card takes. Hover and current are fills (`surface/hover`,
|
|
9
|
+
* `surface/selected`),
|
|
10
|
+
* which is exactly where this parts company with NavItem: NavItem was measured
|
|
11
|
+
* as a header link that recolours and never fills.
|
|
12
|
+
*
|
|
13
|
+
* Written against the contrast gate's known limits (AGENTS.md, "How a
|
|
14
|
+
* stylesheet goes unmeasured"): state is BEM modifiers, the sidebar's surface
|
|
15
|
+
* is on the block so the translucent hover has a ground in this file, and there
|
|
16
|
+
* are no `:not()` lists.
|
|
17
|
+
*/
|
|
18
|
+
.ion-sidebar {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
inline-size: 100%;
|
|
22
|
+
block-size: 100%;
|
|
23
|
+
min-block-size: 0;
|
|
24
|
+
border-inline-end: var(--border-width-default) solid var(--border-subtle);
|
|
25
|
+
background-color: var(--surface-default);
|
|
26
|
+
color: var(--text-secondary);
|
|
27
|
+
font-family: var(--font-family-sans);
|
|
28
|
+
font-size: var(--type-body-sm);
|
|
29
|
+
line-height: var(--type-body-sm-line-height);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.ion-sidebar__header,
|
|
33
|
+
.ion-sidebar__footer {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex: none;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
gap: var(--spacing-8);
|
|
38
|
+
padding: var(--spacing-12);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.ion-sidebar__header {
|
|
42
|
+
border-block-end: var(--border-width-default) solid var(--border-subtle);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.ion-sidebar__footer {
|
|
46
|
+
border-block-start: var(--border-width-default) solid var(--border-subtle);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Only the middle scrolls, so the switcher and Invite stay put. */
|
|
50
|
+
.ion-sidebar__body {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex: 1 1 auto;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: var(--spacing-16);
|
|
55
|
+
min-block-size: 0;
|
|
56
|
+
padding: var(--spacing-12) var(--spacing-8);
|
|
57
|
+
overflow-y: auto;
|
|
58
|
+
overscroll-behavior: contain;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* ---------------------------------------------------------------- section */
|
|
62
|
+
|
|
63
|
+
.ion-sidebar__section {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
gap: var(--spacing-2);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.ion-sidebar__section-header {
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: var(--spacing-4);
|
|
73
|
+
min-block-size: var(--spacing-24);
|
|
74
|
+
padding-inline: var(--spacing-8) var(--spacing-4);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.ion-sidebar__section-title {
|
|
78
|
+
display: inline-flex;
|
|
79
|
+
flex: 1 1 auto;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: var(--spacing-4);
|
|
82
|
+
margin: 0;
|
|
83
|
+
padding: 0;
|
|
84
|
+
border: 0;
|
|
85
|
+
border-radius: var(--radius-xs);
|
|
86
|
+
background: none;
|
|
87
|
+
color: var(--text-tertiary);
|
|
88
|
+
font: inherit;
|
|
89
|
+
font-size: var(--type-caption);
|
|
90
|
+
line-height: var(--type-caption-line-height);
|
|
91
|
+
font-weight: var(--font-weight-medium);
|
|
92
|
+
text-align: start;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
button.ion-sidebar__section-title {
|
|
96
|
+
cursor: pointer;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.ion-sidebar__section-actions {
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
flex: none;
|
|
102
|
+
align-items: center;
|
|
103
|
+
gap: var(--spacing-2);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* -------------------------------------------------------------------- list */
|
|
107
|
+
|
|
108
|
+
.ion-sidebar__list {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
gap: var(--spacing-2);
|
|
112
|
+
margin: 0;
|
|
113
|
+
padding: 0;
|
|
114
|
+
list-style: none;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
* Indentation accumulates by nesting, so depth needs no prop and no inline
|
|
119
|
+
* style: each level is one more padded list inside the last.
|
|
120
|
+
*/
|
|
121
|
+
.ion-sidebar__list--nested {
|
|
122
|
+
margin-block-start: var(--spacing-2);
|
|
123
|
+
padding-inline-start: var(--spacing-16);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* A hidden list is `hidden`; the display above must not override it. */
|
|
127
|
+
.ion-sidebar__list[hidden] {
|
|
128
|
+
display: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* --------------------------------------------------------------------- row */
|
|
132
|
+
|
|
133
|
+
.ion-sidebar__row {
|
|
134
|
+
position: relative;
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
gap: var(--spacing-2);
|
|
138
|
+
min-block-size: var(--spacing-32);
|
|
139
|
+
padding-inline-end: var(--spacing-4);
|
|
140
|
+
border-radius: var(--radius-md);
|
|
141
|
+
color: var(--text-secondary);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.ion-sidebar__link {
|
|
145
|
+
display: flex;
|
|
146
|
+
flex: 1 1 auto;
|
|
147
|
+
align-items: center;
|
|
148
|
+
gap: var(--spacing-8);
|
|
149
|
+
min-inline-size: 0;
|
|
150
|
+
min-block-size: var(--spacing-32);
|
|
151
|
+
margin: 0;
|
|
152
|
+
padding: 0 var(--spacing-8);
|
|
153
|
+
border: 0;
|
|
154
|
+
border-radius: var(--radius-md);
|
|
155
|
+
background: none;
|
|
156
|
+
color: inherit;
|
|
157
|
+
font: inherit;
|
|
158
|
+
text-align: start;
|
|
159
|
+
text-decoration: none;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
a.ion-sidebar__link,
|
|
163
|
+
button.ion-sidebar__link {
|
|
164
|
+
cursor: pointer;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.ion-sidebar__icon {
|
|
168
|
+
display: inline-flex;
|
|
169
|
+
flex: none;
|
|
170
|
+
color: var(--icon-secondary);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.ion-sidebar__icon svg,
|
|
174
|
+
.ion-sidebar__chevron svg {
|
|
175
|
+
width: var(--icon-size-sm);
|
|
176
|
+
height: var(--icon-size-sm);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Long names truncate; the full text is still the link's accessible name. */
|
|
180
|
+
.ion-sidebar__label {
|
|
181
|
+
flex: 1 1 auto;
|
|
182
|
+
min-inline-size: 0;
|
|
183
|
+
overflow: hidden;
|
|
184
|
+
text-overflow: ellipsis;
|
|
185
|
+
white-space: nowrap;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.ion-sidebar__badge {
|
|
189
|
+
flex: none;
|
|
190
|
+
color: var(--text-tertiary);
|
|
191
|
+
font-size: var(--type-caption);
|
|
192
|
+
line-height: var(--type-caption-line-height);
|
|
193
|
+
font-variant-numeric: tabular-nums;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.ion-sidebar__chevron {
|
|
197
|
+
display: inline-flex;
|
|
198
|
+
flex: none;
|
|
199
|
+
color: var(--icon-tertiary);
|
|
200
|
+
transition: transform var(--ion-duration-base) var(--ion-ease-out);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.ion-sidebar__chevron--expanded,
|
|
204
|
+
.ion-sidebar__section-title--expanded .ion-sidebar__chevron {
|
|
205
|
+
transform: rotate(90deg);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* The separate expand button of a row that is also a link. */
|
|
209
|
+
.ion-sidebar__toggle {
|
|
210
|
+
display: inline-flex;
|
|
211
|
+
flex: none;
|
|
212
|
+
align-items: center;
|
|
213
|
+
justify-content: center;
|
|
214
|
+
width: var(--spacing-24);
|
|
215
|
+
height: var(--spacing-24);
|
|
216
|
+
margin: 0;
|
|
217
|
+
padding: 0;
|
|
218
|
+
border: 0;
|
|
219
|
+
border-radius: var(--radius-sm);
|
|
220
|
+
background: none;
|
|
221
|
+
color: inherit;
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/*
|
|
226
|
+
* Row actions. Hidden until the row is hovered or holds focus — `opacity`,
|
|
227
|
+
* not `display` or `visibility`, so they stay in the tab order and appear the
|
|
228
|
+
* moment focus reaches them. Always shown where there is no hover to reveal
|
|
229
|
+
* them, or a touch user could never find them.
|
|
230
|
+
*/
|
|
231
|
+
.ion-sidebar__actions {
|
|
232
|
+
display: inline-flex;
|
|
233
|
+
flex: none;
|
|
234
|
+
align-items: center;
|
|
235
|
+
gap: var(--spacing-2);
|
|
236
|
+
opacity: 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.ion-sidebar__row:hover .ion-sidebar__actions,
|
|
240
|
+
.ion-sidebar__row:focus-within .ion-sidebar__actions {
|
|
241
|
+
opacity: 1;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@media (hover: none) {
|
|
245
|
+
.ion-sidebar__actions {
|
|
246
|
+
opacity: 1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* ------------------------------------------------------------------ states */
|
|
251
|
+
|
|
252
|
+
@media (hover: hover) {
|
|
253
|
+
.ion-sidebar__row:hover {
|
|
254
|
+
background-color: var(--surface-hover);
|
|
255
|
+
color: var(--text-default);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.ion-sidebar__row--current {
|
|
260
|
+
background-color: var(--surface-selected);
|
|
261
|
+
color: var(--text-default);
|
|
262
|
+
font-weight: var(--font-weight-medium);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.ion-sidebar__row--current .ion-sidebar__icon {
|
|
266
|
+
color: var(--icon-primary);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@media (hover: hover) {
|
|
270
|
+
.ion-sidebar__row--current:hover {
|
|
271
|
+
background-color: var(--surface-selected-hover);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/*
|
|
276
|
+
* Two classes, so a disabled row does not react to hover. The sidebar's surface
|
|
277
|
+
* is repeated rather than `transparent`, which the contrast gate would read as an
|
|
278
|
+
* inherited surface and drop — see the note in segmented-control.css.
|
|
279
|
+
*/
|
|
280
|
+
.ion-sidebar .ion-sidebar__row--disabled {
|
|
281
|
+
background-color: var(--surface-default);
|
|
282
|
+
color: var(--text-disabled);
|
|
283
|
+
cursor: not-allowed;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.ion-sidebar .ion-sidebar__row--disabled .ion-sidebar__icon {
|
|
287
|
+
color: var(--icon-disabled);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.ion-sidebar__link:focus-visible,
|
|
291
|
+
.ion-sidebar__toggle:focus-visible,
|
|
292
|
+
.ion-sidebar__section-title:focus-visible {
|
|
293
|
+
outline: var(--border-width-thick) solid var(--border-focus);
|
|
294
|
+
outline-offset: calc(-1 * var(--border-width-thick));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
@media (forced-colors: active) {
|
|
298
|
+
.ion-sidebar__row--current {
|
|
299
|
+
outline: var(--border-width-default) solid;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
@media (prefers-reduced-motion: reduce) {
|
|
304
|
+
.ion-sidebar__chevron {
|
|
305
|
+
transition: none;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Skeleton
|
|
3
|
+
*
|
|
4
|
+
* A pulse on one surface token rather than a sweeping gradient. The gradient
|
|
5
|
+
* version is prettier and costs more than it looks: `verify-contrast.mjs`
|
|
6
|
+
* resolves gradient stops as real pairings, so every skeleton would enter the
|
|
7
|
+
* contrast budget as text-shaped colour it is not — and a placeholder has no
|
|
8
|
+
* text to be legible.
|
|
9
|
+
*/
|
|
10
|
+
.ion-skeleton {
|
|
11
|
+
--ion-skeleton-width: 100%;
|
|
12
|
+
--ion-skeleton-height: var(--spacing-16);
|
|
13
|
+
--ion-skeleton-radius: var(--radius-sm);
|
|
14
|
+
|
|
15
|
+
width: var(--ion-skeleton-width);
|
|
16
|
+
height: var(--ion-skeleton-height);
|
|
17
|
+
background-color: var(--surface-muted);
|
|
18
|
+
border-radius: var(--ion-skeleton-radius);
|
|
19
|
+
animation: ion-skeleton-pulse var(--ion-duration-slow) var(--ion-ease-in-out)
|
|
20
|
+
infinite;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.ion-skeleton-group {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
gap: var(--spacing-8);
|
|
27
|
+
width: var(--ion-skeleton-width, 100%);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Text sits on the line box so a skeleton paragraph occupies the height the
|
|
31
|
+
* real paragraph will, and the layout does not jump when content lands. */
|
|
32
|
+
.ion-skeleton--text {
|
|
33
|
+
--ion-skeleton-height: var(--type-body-line-height);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* The last line short, because real paragraphs end mid-measure. */
|
|
37
|
+
.ion-skeleton--last {
|
|
38
|
+
--ion-skeleton-width: 60%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.ion-skeleton--circle {
|
|
42
|
+
--ion-skeleton-height: var(--icon-size-lg);
|
|
43
|
+
--ion-skeleton-width: var(--icon-size-lg);
|
|
44
|
+
--ion-skeleton-radius: var(--radius-full);
|
|
45
|
+
|
|
46
|
+
flex: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ion-skeleton--rect {
|
|
50
|
+
--ion-skeleton-height: var(--spacing-64);
|
|
51
|
+
--ion-skeleton-radius: var(--radius-md);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@keyframes ion-skeleton-pulse {
|
|
55
|
+
50% {
|
|
56
|
+
opacity: 0.55;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* Held still, unlike Spinner and ProgressBar — and the difference is the point.
|
|
62
|
+
* Those two are the only evidence that a wait is live, so they keep moving
|
|
63
|
+
* slowly. A skeleton says nothing a static block does not, so for a user who
|
|
64
|
+
* asked for less motion it simply stops.
|
|
65
|
+
*/
|
|
66
|
+
@media (prefers-reduced-motion: reduce) {
|
|
67
|
+
.ion-skeleton {
|
|
68
|
+
animation: none;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@media (forced-colors: active) {
|
|
73
|
+
.ion-skeleton {
|
|
74
|
+
border: var(--border-width-default) solid GrayText;
|
|
75
|
+
}
|
|
76
|
+
}
|