@smwb/ui-styles 1.5.1 → 2.0.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/README.md +210 -12
- package/less/components/appBar/appBar.less +108 -0
- package/less/components/avatar/avatar.less +22 -0
- package/less/components/button/button.less +7 -1
- package/less/components/card/card.less +15 -4
- package/less/components/chart/chart.less +16 -15
- package/less/components/checkbox/checkbox.less +55 -4
- package/less/components/chip/chip.less +48 -3
- package/less/components/commandPalette/commandPalette.entry.less +2 -0
- package/less/components/commandPalette/commandPalette.less +233 -0
- package/less/components/composer/composer.entry.less +2 -0
- package/less/components/composer/composer.less +294 -0
- package/less/components/composer/composer.variables.less +10 -0
- package/less/components/dagView/dagView.entry.less +2 -0
- package/less/components/dagView/dagView.less +280 -0
- package/less/components/descriptionList/descriptionList.less +7 -1
- package/less/components/diffStat/diffStat.entry.less +2 -0
- package/less/components/diffStat/diffStat.less +76 -0
- package/less/components/kbd/kbd.entry.less +2 -0
- package/less/components/kbd/kbd.less +55 -0
- package/less/components/list/list.less +39 -0
- package/less/components/menu/menu.less +25 -1
- package/less/components/personChip/personChip.less +4 -2
- package/less/components/qrCode/qrCode.entry.less +2 -0
- package/less/components/qrCode/qrCode.less +35 -0
- package/less/components/radioButton/radioButton.less +46 -1
- package/less/components/rating/rating.less +15 -2
- package/less/components/selectField/selectField.less +87 -5
- package/less/components/sidebar/sidebar.less +26 -0
- package/less/components/statusDot/statusDot.entry.less +2 -0
- package/less/components/statusDot/statusDot.less +118 -0
- package/less/components/swimlanes/swimlanes.entry.less +2 -0
- package/less/components/swimlanes/swimlanes.less +126 -0
- package/less/components/tabGroups/tabGroups.entry.less +2 -0
- package/less/components/tabGroups/tabGroups.less +219 -0
- package/less/components/table/table.less +12 -0
- package/less/components/tabs/tab/tab.less +128 -0
- package/less/components/tabs/tabPanel/tabPanel.entry.less +2 -0
- package/less/components/tabs/tabPanel/tabPanel.less +8 -0
- package/less/components/tabs/tabs.less +26 -0
- package/less/components/textField/textField.less +34 -9
- package/less/components/thumbCard/thumbCard.less +12 -2
- package/less/components/toggle/toggle.less +42 -2
- package/less/components/treeView/treeView.less +40 -2
- package/less/components/typography/typography.less +13 -6
- package/less/components.less +10 -0
- package/less/connects/rt/table.less +7 -0
- package/less/default/globals/default.variables.less +152 -1
- package/less/foundation/sizing.less +41 -5
- package/less/foundation/typography.less +21 -0
- package/less/themes/material/variables.less +1 -0
- package/less/themes/summer/variables.less +1 -0
- package/less/variables.less +5 -0
- package/package.json +5 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
.smwb-chip {
|
|
2
2
|
position: relative;
|
|
3
3
|
user-select: none;
|
|
4
|
-
|
|
4
|
+
// Chip's default box is 32px, which is the sm step of the control scale, not md
|
|
5
|
+
// (Chip has no large size, so sm is the only step it maps to).
|
|
6
|
+
min-height: var(--smui-control-h-sm, @smui-control-h-sm);
|
|
5
7
|
display: inline-flex;
|
|
6
8
|
align-items: center;
|
|
7
9
|
justify-content: center;
|
|
@@ -33,7 +35,10 @@
|
|
|
33
35
|
}
|
|
34
36
|
&:active {
|
|
35
37
|
background-color: var(--smui-chipsActiveBg);
|
|
36
|
-
box-shadow:
|
|
38
|
+
box-shadow:
|
|
39
|
+
0px 3px 5px -1px rgb(0 0 0 / 16%),
|
|
40
|
+
0px 1px 1px 0px rgb(0 0 0 / 14%),
|
|
41
|
+
0px 2px 3px 0px rgb(0 0 0 / 12%);
|
|
37
42
|
}
|
|
38
43
|
&.smwb-selected {
|
|
39
44
|
background-color: var(--smui-chipsSelectedBg);
|
|
@@ -47,7 +52,10 @@
|
|
|
47
52
|
}
|
|
48
53
|
&:active {
|
|
49
54
|
background-color: var(--smui-chipsActiveBg);
|
|
50
|
-
box-shadow:
|
|
55
|
+
box-shadow:
|
|
56
|
+
0px 3px 5px -1px rgb(0 0 0 / 11%),
|
|
57
|
+
0px 1px 1px 0px rgb(0 0 0 / 14%),
|
|
58
|
+
0px 2px 3px 0px rgb(0 0 0 / 12%);
|
|
51
59
|
}
|
|
52
60
|
&.smwb-selected {
|
|
53
61
|
background-color: var(--smui-chipsSelectedBg);
|
|
@@ -74,6 +82,39 @@
|
|
|
74
82
|
justify-content: center;
|
|
75
83
|
}
|
|
76
84
|
|
|
85
|
+
// The icon and the label: the chip's button when it has onClick. The delete
|
|
86
|
+
// button follows it, beside the button, not inside it.
|
|
87
|
+
&__content {
|
|
88
|
+
display: inline-flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: @chipGap;
|
|
91
|
+
border-radius: inherit;
|
|
92
|
+
|
|
93
|
+
// The button's focus ring goes around the whole chip, as when the chip
|
|
94
|
+
// itself took focus: its own ring is pushed out of sight, and ::after, laid
|
|
95
|
+
// over the chip (the chip is its containing block), takes its outline.
|
|
96
|
+
// Whatever sets that outline still decides it: the browser's ring, an
|
|
97
|
+
// app's, or none under a reset that removes focus rings (normalize.css).
|
|
98
|
+
&:focus {
|
|
99
|
+
outline-offset: 9999px;
|
|
100
|
+
}
|
|
101
|
+
&:focus::after {
|
|
102
|
+
content: "";
|
|
103
|
+
position: absolute;
|
|
104
|
+
inset: 0;
|
|
105
|
+
border-radius: inherit;
|
|
106
|
+
outline: inherit;
|
|
107
|
+
pointer-events: none;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// An outlined chip's ring sits where the chip's own did: ::after takes the
|
|
111
|
+
// chip's box and a border as wide as its border, as the browser draws its
|
|
112
|
+
// ring over a border and an app's ring around it.
|
|
113
|
+
&.smwb-outlined > &__content:focus::after {
|
|
114
|
+
inset: -1px;
|
|
115
|
+
border: 1px solid transparent;
|
|
116
|
+
}
|
|
117
|
+
|
|
77
118
|
// delete control is a <button> now — strip UA chrome (additive)
|
|
78
119
|
&__delete-icon {
|
|
79
120
|
background: transparent;
|
|
@@ -86,7 +127,11 @@
|
|
|
86
127
|
}
|
|
87
128
|
|
|
88
129
|
&_size-small {
|
|
130
|
+
// 24px is deliberately below the control scale (Fork C): it stays a literal,
|
|
131
|
+
// not a token. min-height is repeated because the base rule floors the box at
|
|
132
|
+
// the sm step (32px); without it the height alone would be clamped back up.
|
|
89
133
|
height: 24px;
|
|
134
|
+
min-height: 24px;
|
|
90
135
|
padding: 0 @smui-space-sm;
|
|
91
136
|
font-size: 0.75rem;
|
|
92
137
|
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// The ⌘K palette. It is a Modal, so its box is overridden through a
|
|
2
|
+
// three-class selector: `.smwb-modal.smwb-medium` is (0,2,0), and which of two
|
|
3
|
+
// equal-specificity sheets wins differs between a Storybook dev server and a
|
|
4
|
+
// build, so a two-class rule would only win by luck.
|
|
5
|
+
.smwb-modal__overlay > .smwb-modal.smwb-command-palette {
|
|
6
|
+
width: @commandPaletteWidth;
|
|
7
|
+
height: auto;
|
|
8
|
+
min-height: 0;
|
|
9
|
+
max-height: @commandPaletteMaxHeight;
|
|
10
|
+
// The results box scrolls, not the dialog, so the footer stays put.
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
padding: 0;
|
|
13
|
+
// Modal's own radius. Its small-screen rule zeroes it for a full-bleed dialog;
|
|
14
|
+
// the palette keeps side gutters there instead, so it keeps the corners too.
|
|
15
|
+
border-radius: @modalBorderRadius;
|
|
16
|
+
// A cross-axis auto margin beats the overlay's `align-items: center`, which is
|
|
17
|
+
// what puts the palette near the top instead of in the middle.
|
|
18
|
+
margin-block: @commandPaletteTopOffset auto;
|
|
19
|
+
|
|
20
|
+
@media (max-width: 600px) {
|
|
21
|
+
margin-block-start: @smui-space-lg;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.smwb-command-palette {
|
|
26
|
+
&__panel {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
flex: 1 1 auto;
|
|
30
|
+
min-height: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&__search {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: @smui-space-sm;
|
|
37
|
+
padding: @smui-space-md @smui-space-lg;
|
|
38
|
+
border-bottom: 1px solid var(--smui-dividerColor);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&__search-icon {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
flex-shrink: 0;
|
|
44
|
+
color: var(--smui-onSurfaceVariant);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The caret is a text field's focus indicator, so the box draws no ring of its
|
|
48
|
+
// own (normalize.css removes the browser's outline anyway).
|
|
49
|
+
&__input {
|
|
50
|
+
flex: 1;
|
|
51
|
+
min-width: 0;
|
|
52
|
+
border: 0;
|
|
53
|
+
background: transparent;
|
|
54
|
+
font: inherit;
|
|
55
|
+
font-size: 1rem;
|
|
56
|
+
line-height: 1.5;
|
|
57
|
+
color: var(--smui-onSurfaceColor);
|
|
58
|
+
outline: none;
|
|
59
|
+
|
|
60
|
+
&::placeholder {
|
|
61
|
+
color: var(--smui-onSurfaceVariant);
|
|
62
|
+
opacity: 1; // the UA grey would stay light in the dark theme
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&__results {
|
|
67
|
+
flex: 1 1 auto;
|
|
68
|
+
min-height: 0;
|
|
69
|
+
overflow-y: auto;
|
|
70
|
+
overscroll-behavior: contain; // a palette over a page does not scroll it
|
|
71
|
+
padding: @smui-space-xs 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&__listbox,
|
|
75
|
+
&__group-options {
|
|
76
|
+
list-style: none;
|
|
77
|
+
margin: 0;
|
|
78
|
+
padding: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// As SelectField's group label, so a grouped list reads the same in both.
|
|
82
|
+
&__group-label {
|
|
83
|
+
padding: @smui-space-sm @smui-space-lg @smui-space-xs;
|
|
84
|
+
font-size: 12px;
|
|
85
|
+
font-weight: 600;
|
|
86
|
+
text-transform: uppercase;
|
|
87
|
+
letter-spacing: 0.04em;
|
|
88
|
+
color: var(--smui-onSurfaceVariant);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&__item {
|
|
92
|
+
position: relative; // holds the state layer
|
|
93
|
+
display: grid;
|
|
94
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
95
|
+
align-items: center;
|
|
96
|
+
gap: @smui-space-md;
|
|
97
|
+
min-height: @commandPaletteItemMinHeight;
|
|
98
|
+
padding: @smui-space-xs @smui-space-lg;
|
|
99
|
+
margin: 0 @smui-space-xs;
|
|
100
|
+
border-radius: @commandPaletteItemRadius;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
color: var(--smui-onSurfaceColor);
|
|
103
|
+
|
|
104
|
+
&::before {
|
|
105
|
+
content: "";
|
|
106
|
+
position: absolute;
|
|
107
|
+
inset: 0;
|
|
108
|
+
border-radius: inherit;
|
|
109
|
+
background: currentColor;
|
|
110
|
+
opacity: 0;
|
|
111
|
+
pointer-events: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
&:hover:not(&_active):not(&_disabled)::before {
|
|
115
|
+
opacity: @commandPaletteHoverLayerOpacity;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The option the keys are on. Focus stays in the field, which points here
|
|
119
|
+
// with aria-activedescendant, so this row draws the library's ring inside
|
|
120
|
+
// itself: a scrolling results box would clip an outside one. The state layer
|
|
121
|
+
// is not an indicator on its own (1.29:1 against a plain row) and forced
|
|
122
|
+
// colours flatten it to Canvas, hence the outline.
|
|
123
|
+
&_active {
|
|
124
|
+
outline: 2px solid var(--smui-primaryColor);
|
|
125
|
+
outline-offset: -2px;
|
|
126
|
+
|
|
127
|
+
&::before {
|
|
128
|
+
opacity: @commandPaletteActiveLayerOpacity;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@media (forced-colors: active) {
|
|
132
|
+
outline-color: Highlight;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
&_disabled {
|
|
137
|
+
color: var(--smui-disabledTextColor);
|
|
138
|
+
cursor: default;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
&__item-icon {
|
|
143
|
+
grid-column: 1;
|
|
144
|
+
display: inline-flex;
|
|
145
|
+
align-items: center;
|
|
146
|
+
justify-content: center;
|
|
147
|
+
width: @commandPaletteIconSize;
|
|
148
|
+
height: @commandPaletteIconSize;
|
|
149
|
+
color: var(--smui-onSurfaceVariant);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
&__item-text {
|
|
153
|
+
grid-column: 2;
|
|
154
|
+
min-width: 0; // lets the two lines ellipsis instead of widening the track
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// With no icon the text takes the icon's column as well, so a row without one
|
|
158
|
+
// is not indented against an empty gutter. A browser without :has() drops the
|
|
159
|
+
// rule and keeps the text in column 2, which the empty `auto` track collapses.
|
|
160
|
+
&__item:not(:has(> .smwb-command-palette__item-icon)) > .smwb-command-palette__item-text {
|
|
161
|
+
grid-column: 1 / 3;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
&__item-label {
|
|
165
|
+
display: block;
|
|
166
|
+
overflow: hidden;
|
|
167
|
+
text-overflow: ellipsis;
|
|
168
|
+
white-space: nowrap;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
&__item-subtitle {
|
|
172
|
+
display: block;
|
|
173
|
+
overflow: hidden;
|
|
174
|
+
text-overflow: ellipsis;
|
|
175
|
+
white-space: nowrap;
|
|
176
|
+
font-size: 0.8125rem;
|
|
177
|
+
color: var(--smui-onSurfaceVariant);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Two classes: Kbd sets `font-size: inherit` at the same specificity, and in
|
|
181
|
+
// Storybook the per-component sheets can land in either order.
|
|
182
|
+
&__item-shortcut.smwb-kbd {
|
|
183
|
+
grid-column: 3;
|
|
184
|
+
font-size: 0.75rem;
|
|
185
|
+
color: var(--smui-onSurfaceVariant);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// The rows a pending search draws in place of results. Skeleton is already
|
|
189
|
+
// aria-hidden, and the item grid keeps the widths the real rows will have, so
|
|
190
|
+
// the list does not reflow when the answer lands. No hover layer: nothing here
|
|
191
|
+
// can be chosen.
|
|
192
|
+
&__loading-row {
|
|
193
|
+
display: grid;
|
|
194
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
195
|
+
align-items: center;
|
|
196
|
+
gap: @smui-space-md;
|
|
197
|
+
min-height: @commandPaletteItemMinHeight;
|
|
198
|
+
padding: @smui-space-xs @smui-space-lg;
|
|
199
|
+
margin: 0 @smui-space-xs;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
&__message {
|
|
203
|
+
padding: @smui-space-lg @smui-space-xl;
|
|
204
|
+
text-align: center;
|
|
205
|
+
color: var(--smui-onSurfaceVariant);
|
|
206
|
+
|
|
207
|
+
&_error {
|
|
208
|
+
color: var(--smui-errorColor);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
&__footer {
|
|
213
|
+
display: flex;
|
|
214
|
+
align-items: center;
|
|
215
|
+
gap: @smui-space-md;
|
|
216
|
+
padding: @smui-space-sm @smui-space-lg;
|
|
217
|
+
border-top: 1px solid var(--smui-dividerColor);
|
|
218
|
+
font-size: 0.75rem;
|
|
219
|
+
color: var(--smui-onSurfaceVariant);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// The polite live region. A screen reader ignores one outside an aria-modal
|
|
223
|
+
// dialog, so it lives inside the palette and is hidden from sight only. With
|
|
224
|
+
// no inset it stays at its static position, out of flow.
|
|
225
|
+
&__status {
|
|
226
|
+
position: absolute;
|
|
227
|
+
width: 1px;
|
|
228
|
+
height: 1px;
|
|
229
|
+
overflow: hidden;
|
|
230
|
+
clip-path: inset(50%);
|
|
231
|
+
white-space: nowrap;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
@import "./composer.variables.less";
|
|
2
|
+
|
|
3
|
+
// The textarea and its mirror (.smwb-composer__field::after) must share these,
|
|
4
|
+
// or the auto-grown height drifts from the text.
|
|
5
|
+
.smwb-composer-text() {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
margin: 0;
|
|
8
|
+
border: 0;
|
|
9
|
+
padding: @composerFieldPaddingY @composerFieldPaddingX;
|
|
10
|
+
font: inherit;
|
|
11
|
+
letter-spacing: inherit;
|
|
12
|
+
line-height: @composerLineHeight;
|
|
13
|
+
overflow-wrap: anywhere;
|
|
14
|
+
// The height of N rows of text, with the text's own padding.
|
|
15
|
+
min-height: ~"calc(var(--smwb-composer-min-rows, 1) * @{composerLineHeight} + 2 * @{composerFieldPaddingY})";
|
|
16
|
+
max-height: ~"calc(var(--smwb-composer-max-rows, 8) * @{composerLineHeight} + 2 * @{composerFieldPaddingY})";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// A task input: a growing textarea with an attach button, a toolbar slot, a
|
|
20
|
+
// counter and a send/stop button. The children are flat; grid areas place them
|
|
21
|
+
// for each layout. An absent child leaves its area empty, and the spacing is
|
|
22
|
+
// margins, not gaps, so an empty track adds none.
|
|
23
|
+
.smwb-composer {
|
|
24
|
+
position: relative;
|
|
25
|
+
display: grid;
|
|
26
|
+
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
|
27
|
+
align-items: center;
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
width: 100%;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
padding: @composerPadding;
|
|
32
|
+
// Focus (below) paints the second pixel of the border inside, so the layout
|
|
33
|
+
// does not shift.
|
|
34
|
+
border: 1px solid @composerBorder;
|
|
35
|
+
border-radius: @composerRadius;
|
|
36
|
+
background-color: @composerBg;
|
|
37
|
+
color: @composerText;
|
|
38
|
+
|
|
39
|
+
&__attachments {
|
|
40
|
+
grid-area: attachments;
|
|
41
|
+
min-width: 0;
|
|
42
|
+
margin-bottom: @composerGap;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&__field {
|
|
46
|
+
grid-area: field;
|
|
47
|
+
display: grid;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
|
|
50
|
+
// The mirror: the text plus a trailing space (so a final newline counts a
|
|
51
|
+
// line) in the same box as the textarea. It sizes the grid cell, and the
|
|
52
|
+
// textarea fills it, so the field grows with the text up to max-height.
|
|
53
|
+
&::after {
|
|
54
|
+
.smwb-composer-text();
|
|
55
|
+
content: attr(data-value) " ";
|
|
56
|
+
grid-area: 1 / 1;
|
|
57
|
+
visibility: hidden;
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
white-space: pre-wrap;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&__input {
|
|
65
|
+
.smwb-composer-text();
|
|
66
|
+
grid-area: 1 / 1;
|
|
67
|
+
display: block;
|
|
68
|
+
width: 100%;
|
|
69
|
+
resize: none;
|
|
70
|
+
overflow-y: auto;
|
|
71
|
+
outline: 0;
|
|
72
|
+
background: transparent;
|
|
73
|
+
color: @composerText;
|
|
74
|
+
caret-color: @composerText;
|
|
75
|
+
|
|
76
|
+
&::placeholder {
|
|
77
|
+
// The UA grey stays light in the dark theme.
|
|
78
|
+
color: @composerPlaceholder;
|
|
79
|
+
opacity: 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
&__icon-button {
|
|
84
|
+
display: inline-flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
box-sizing: border-box;
|
|
88
|
+
flex: none;
|
|
89
|
+
width: @composerIconButtonSize;
|
|
90
|
+
height: @composerIconButtonSize;
|
|
91
|
+
margin: 0;
|
|
92
|
+
padding: 0;
|
|
93
|
+
border: 0;
|
|
94
|
+
border-radius: @smui-radius-circle;
|
|
95
|
+
background-color: transparent;
|
|
96
|
+
color: @composerText;
|
|
97
|
+
font: inherit;
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
user-select: none;
|
|
100
|
+
-webkit-user-select: none;
|
|
101
|
+
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
|
102
|
+
|
|
103
|
+
&:hover:not(:disabled):not([aria-disabled="true"]) {
|
|
104
|
+
background-color: var(--smui-onSurfaceHover);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
&:active:not(:disabled):not([aria-disabled="true"]) {
|
|
108
|
+
background-color: var(--smui-onSurfaceActive);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// normalize.css removes outlines, so each focusable draws its own ring.
|
|
112
|
+
&:focus-visible {
|
|
113
|
+
outline: 2px solid @composerBorderFocus;
|
|
114
|
+
outline-offset: 2px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
&:disabled {
|
|
118
|
+
cursor: default;
|
|
119
|
+
pointer-events: none;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
&__attach {
|
|
124
|
+
grid-area: attach;
|
|
125
|
+
color: @composerCounter;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
&__toolbar {
|
|
129
|
+
grid-area: toolbar;
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-wrap: wrap;
|
|
132
|
+
align-items: center;
|
|
133
|
+
gap: @composerGap;
|
|
134
|
+
min-width: 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// The toolbar follows the attach button.
|
|
138
|
+
&__attach ~ &__toolbar {
|
|
139
|
+
margin-inline-start: @composerGap;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
&__counter {
|
|
143
|
+
grid-area: counter;
|
|
144
|
+
margin-inline-start: @composerGap;
|
|
145
|
+
color: @composerCounter;
|
|
146
|
+
font-size: 0.75rem;
|
|
147
|
+
line-height: 1rem;
|
|
148
|
+
font-variant-numeric: tabular-nums;
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
|
|
151
|
+
&_error {
|
|
152
|
+
color: @composerCounterError;
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
&__action {
|
|
158
|
+
grid-area: action;
|
|
159
|
+
margin-inline-start: @composerGap;
|
|
160
|
+
|
|
161
|
+
// Send is available: filled.
|
|
162
|
+
&:not([aria-disabled="true"]):not(:disabled) {
|
|
163
|
+
background-color: @composerActionBg;
|
|
164
|
+
color: @composerActionColor;
|
|
165
|
+
|
|
166
|
+
&:hover {
|
|
167
|
+
background-color: ~"color-mix(in srgb, @{composerActionColor} 12%, @{composerActionBg})";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
&:active {
|
|
171
|
+
background-color: ~"color-mix(in srgb, @{composerActionColor} 20%, @{composerActionBg})";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Send is unavailable (empty text, over the limit, no handler): muted. The
|
|
176
|
+
// button is not natively disabled, so it keeps focus.
|
|
177
|
+
&[aria-disabled="true"],
|
|
178
|
+
&:disabled {
|
|
179
|
+
background-color: var(--smui-disabledBgColor);
|
|
180
|
+
color: var(--smui-disabledTextColor);
|
|
181
|
+
cursor: default;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Stop: the agent is busy.
|
|
185
|
+
&_stop:not([aria-disabled="true"]):not(:disabled) {
|
|
186
|
+
background-color: @composerStopBg;
|
|
187
|
+
color: @composerStopColor;
|
|
188
|
+
|
|
189
|
+
&:hover {
|
|
190
|
+
background-color: @composerStopBg;
|
|
191
|
+
opacity: 0.88;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
&:active {
|
|
195
|
+
background-color: @composerStopBg;
|
|
196
|
+
opacity: 0.76;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// The live region for the counter: read by screen readers, never drawn.
|
|
202
|
+
&__status {
|
|
203
|
+
position: absolute;
|
|
204
|
+
width: 1px;
|
|
205
|
+
height: 1px;
|
|
206
|
+
margin: -1px;
|
|
207
|
+
padding: 0;
|
|
208
|
+
border: 0;
|
|
209
|
+
overflow: hidden;
|
|
210
|
+
clip-path: inset(50%);
|
|
211
|
+
white-space: nowrap;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ---- Layouts ----
|
|
215
|
+
// stacked: the field on top; a bar under it (attach, toolbar, counter, action).
|
|
216
|
+
&_stacked {
|
|
217
|
+
grid-template-areas:
|
|
218
|
+
"attachments attachments attachments attachments"
|
|
219
|
+
"field field field field"
|
|
220
|
+
"attach toolbar counter action";
|
|
221
|
+
|
|
222
|
+
.smwb-composer__field {
|
|
223
|
+
margin-bottom: @composerGap;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.smwb-composer__action {
|
|
227
|
+
justify-self: end;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// inline: one row (attach, field, counter, action); the toolbar, if any, under it.
|
|
232
|
+
&_inline {
|
|
233
|
+
grid-template-areas:
|
|
234
|
+
"attachments attachments attachments attachments"
|
|
235
|
+
"attach field counter action"
|
|
236
|
+
"toolbar toolbar toolbar toolbar";
|
|
237
|
+
// Buttons stay at the bottom edge as the field grows.
|
|
238
|
+
align-items: end;
|
|
239
|
+
|
|
240
|
+
.smwb-composer__toolbar {
|
|
241
|
+
margin-top: @composerGap;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.smwb-composer__counter {
|
|
245
|
+
// On the bottom row of text, level with the action.
|
|
246
|
+
align-self: end;
|
|
247
|
+
padding-bottom: ~"calc((@{composerIconButtonSize} - 1rem) / 2)";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.smwb-composer__attach ~ .smwb-composer__toolbar {
|
|
251
|
+
margin-inline-start: 0;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ---- States ----
|
|
256
|
+
// Set from the textarea's focus, not :focus-within: the chips in the toolbar
|
|
257
|
+
// have rings of their own.
|
|
258
|
+
&_focused {
|
|
259
|
+
border-color: @composerBorderFocus;
|
|
260
|
+
box-shadow: inset 0 0 0 1px @composerBorderFocus;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
&_drag-active {
|
|
264
|
+
border-style: dashed;
|
|
265
|
+
border-color: @composerBorderFocus;
|
|
266
|
+
background-color: @composerDragBg;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
&_disabled {
|
|
270
|
+
opacity: 0.6;
|
|
271
|
+
cursor: default;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
&_over-limit:not(.smwb-composer_focused) {
|
|
275
|
+
border-color: @composerCounterError;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@media (forced-colors: active) {
|
|
279
|
+
&_focused {
|
|
280
|
+
outline: 2px solid Highlight;
|
|
281
|
+
outline-offset: -2px;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
&__action:not([aria-disabled="true"]):not(:disabled) {
|
|
285
|
+
border: 1px solid ButtonText;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// The UA greys a native disabled control; an unavailable Send is aria-disabled, so it needs the same grey.
|
|
289
|
+
&__action[aria-disabled="true"],
|
|
290
|
+
&__action:disabled {
|
|
291
|
+
color: GrayText;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
@composerPadding: @smui-space-sm;
|
|
2
|
+
@composerGap: @smui-space-xs;
|
|
3
|
+
@composerRadius: @smui-radius-lg;
|
|
4
|
+
// One text line. The textarea and its mirror share it, so their heights agree.
|
|
5
|
+
@composerLineHeight: 1.5rem;
|
|
6
|
+
// The text's own padding inside the field; with one line, 24px + 2 * 6px = 36px,
|
|
7
|
+
// the height of an icon button.
|
|
8
|
+
@composerFieldPaddingY: 6px;
|
|
9
|
+
@composerFieldPaddingX: @smui-space-sm;
|
|
10
|
+
@composerIconButtonSize: 36px;
|