@gjsify/adwaita-web 0.36.0 → 0.37.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/adwaita-web.css +433 -33
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-shortcut-label.spec.d.ts +2 -0
- package/lib/types/breakpoints.d.ts +10 -12
- package/lib/types/elements/adw-about-dialog.d.ts +19 -36
- package/lib/types/elements/adw-action-row.d.ts +9 -12
- package/lib/types/elements/adw-alert-dialog.d.ts +0 -12
- package/lib/types/elements/adw-avatar.d.ts +1 -0
- package/lib/types/elements/adw-bottom-sheet.d.ts +7 -12
- package/lib/types/elements/adw-button-content.d.ts +3 -4
- package/lib/types/elements/adw-button-row.d.ts +1 -1
- package/lib/types/elements/adw-carousel.d.ts +28 -49
- package/lib/types/elements/adw-checks.d.ts +2 -8
- package/lib/types/elements/adw-combo-row.d.ts +6 -6
- package/lib/types/elements/adw-data-grid.d.ts +5 -10
- package/lib/types/elements/adw-drop-down.d.ts +3 -11
- package/lib/types/elements/adw-entry-row.d.ts +20 -23
- package/lib/types/elements/adw-icon.d.ts +2 -4
- package/lib/types/elements/adw-inline-view-switcher.d.ts +18 -31
- package/lib/types/elements/adw-navigation-split-view.d.ts +7 -12
- package/lib/types/elements/adw-navigation-view.d.ts +2 -10
- package/lib/types/elements/adw-overlay-split-view.d.ts +28 -52
- package/lib/types/elements/adw-password-entry-row.d.ts +4 -5
- package/lib/types/elements/adw-popover.d.ts +2 -7
- package/lib/types/elements/adw-preferences-dialog.d.ts +21 -34
- package/lib/types/elements/adw-preferences-group.d.ts +12 -14
- package/lib/types/elements/adw-progress-bar.d.ts +3 -4
- package/lib/types/elements/adw-shortcut-label.d.ts +20 -0
- package/lib/types/elements/adw-sidebar.d.ts +5 -10
- package/lib/types/elements/adw-spinner.d.ts +3 -5
- package/lib/types/elements/adw-split-button.d.ts +6 -16
- package/lib/types/elements/adw-switch-row.d.ts +3 -3
- package/lib/types/elements/adw-tab-view.d.ts +16 -26
- package/lib/types/elements/adw-toast-overlay.d.ts +8 -13
- package/lib/types/elements/adw-view-stack.d.ts +7 -17
- package/lib/types/elements/adw-view-switcher-bar.d.ts +4 -8
- package/lib/types/elements/adw-view-switcher.d.ts +18 -38
- package/lib/types/elements/adw-wrap-box.d.ts +7 -17
- package/lib/types/elements/view-switcher-dom.d.ts +10 -13
- package/lib/types/index.d.ts +1 -0
- package/lib/types/source-view/hex.d.ts +7 -13
- package/lib/types/style-classes.spec.d.ts +11 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/package.json +6 -6
- package/scss/_about_dialog.scss +3 -2
- package/scss/_action_row.scss +33 -0
- package/scss/_avatar.scss +12 -0
- package/scss/_button.scss +44 -22
- package/scss/_card.scss +11 -8
- package/scss/_checks.scss +2 -4
- package/scss/_dialog.scss +3 -2
- package/scss/_headerbar.scss +23 -0
- package/scss/_popover.scss +8 -4
- package/scss/_preferences.scss +7 -7
- package/scss/_progress_bar.scss +9 -6
- package/scss/_reset.scss +1 -1
- package/scss/_shortcut_label.scss +80 -0
- package/scss/_sidebar.scss +57 -0
- package/scss/_status_page.scss +20 -0
- package/scss/_style_classes.scss +407 -0
- package/scss/_switch.scss +4 -3
- package/scss/_variables.scss +25 -0
- package/scss/adwaita-skin.scss +12 -0
- package/src/adw-about-dialog.spec.ts +34 -49
- package/src/adw-action-rows.spec.ts +25 -33
- package/src/adw-avatar.spec.ts +51 -20
- package/src/adw-banner.spec.ts +5 -8
- package/src/adw-button-content.spec.ts +9 -12
- package/src/adw-button-row.spec.ts +2 -4
- package/src/adw-carousel.spec.ts +40 -53
- package/src/adw-checks.spec.ts +36 -47
- package/src/adw-drop-down.spec.ts +25 -32
- package/src/adw-header-bar.spec.ts +5 -7
- package/src/adw-icon.spec.ts +23 -32
- package/src/adw-progress-bar.spec.ts +10 -12
- package/src/adw-row-state.spec.ts +9 -10
- package/src/adw-shortcut-label.spec.ts +112 -0
- package/src/adw-sidebar.spec.ts +17 -24
- package/src/adw-switch.spec.ts +11 -17
- package/src/adw-tab-view.spec.ts +7 -10
- package/src/adw-toast-overlay.spec.ts +7 -11
- package/src/adw-view-switcher-bar.spec.ts +10 -14
- package/src/adw-wrap-box.spec.ts +25 -36
- package/src/bottom-sheet.spec.ts +30 -36
- package/src/breakpoints.ts +15 -18
- package/src/chrome.spec.ts +13 -21
- package/src/elements/adw-about-dialog.ts +71 -114
- package/src/elements/adw-action-row.ts +22 -37
- package/src/elements/adw-alert-dialog.ts +25 -50
- package/src/elements/adw-avatar.ts +40 -15
- package/src/elements/adw-banner.ts +14 -18
- package/src/elements/adw-bottom-sheet.ts +31 -52
- package/src/elements/adw-button-content.ts +30 -37
- package/src/elements/adw-button-row.ts +14 -24
- package/src/elements/adw-carousel.ts +87 -148
- package/src/elements/adw-checks.ts +43 -56
- package/src/elements/adw-clamp.ts +8 -9
- package/src/elements/adw-combo-row.ts +17 -25
- package/src/elements/adw-data-grid.ts +26 -38
- package/src/elements/adw-dialog.ts +25 -56
- package/src/elements/adw-drop-down.ts +31 -51
- package/src/elements/adw-entry-row.ts +39 -46
- package/src/elements/adw-expander-row.ts +30 -53
- package/src/elements/adw-icon.ts +30 -53
- package/src/elements/adw-inline-view-switcher.ts +38 -67
- package/src/elements/adw-menu-button.ts +28 -58
- package/src/elements/adw-navigation-split-view.ts +35 -47
- package/src/elements/adw-navigation-view.ts +12 -28
- package/src/elements/adw-overlay-split-view.ts +100 -175
- package/src/elements/adw-password-entry-row.ts +18 -28
- package/src/elements/adw-popover.ts +28 -44
- package/src/elements/adw-preferences-dialog.ts +41 -66
- package/src/elements/adw-preferences-group.ts +33 -46
- package/src/elements/adw-progress-bar.ts +27 -47
- package/src/elements/adw-shortcut-label.ts +138 -0
- package/src/elements/adw-sidebar.ts +31 -39
- package/src/elements/adw-spin-row.ts +7 -14
- package/src/elements/adw-spinner.ts +31 -43
- package/src/elements/adw-split-button.ts +45 -75
- package/src/elements/adw-switch-row.ts +29 -46
- package/src/elements/adw-switch.ts +28 -47
- package/src/elements/adw-tab-view.ts +62 -103
- package/src/elements/adw-toast-overlay.ts +28 -35
- package/src/elements/adw-toggle-group.ts +9 -16
- package/src/elements/adw-view-stack.ts +32 -51
- package/src/elements/adw-view-switcher-bar.ts +26 -50
- package/src/elements/adw-view-switcher.ts +60 -110
- package/src/elements/adw-window-title.ts +4 -4
- package/src/elements/adw-wrap-box.ts +33 -59
- package/src/elements/view-switcher-dom.ts +15 -22
- package/src/entry-rows.spec.ts +18 -24
- package/src/index.ts +13 -15
- package/src/navigation-view.spec.ts +7 -10
- package/src/preferences-search.ts +11 -15
- package/src/preferences.spec.ts +13 -15
- package/src/source-view/adw-source-view.ts +6 -19
- package/src/source-view/hex.ts +7 -13
- package/src/split-button.spec.ts +32 -44
- package/src/split-views.spec.ts +83 -149
- package/src/style-classes.spec.ts +181 -0
- package/src/style-isolation.spec.ts +3 -6
- package/src/styles.generated.ts +1 -1
- package/src/tab-view.spec.ts +26 -35
- package/src/test.browser.mts +4 -0
- package/src/view-stack.spec.ts +23 -34
- package/src/view-switcher.spec.ts +56 -84
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
// _style_classes.scss — libadwaita's documented, WIDGET-INDEPENDENT style classes.
|
|
2
|
+
//
|
|
3
|
+
// THE GAP THIS CLOSES
|
|
4
|
+
//
|
|
5
|
+
// `refs/libadwaita/doc/style-classes.md` is the public contract an app author
|
|
6
|
+
// reads: put `.card` on a box, `.boxed-list` on a list, `.toolbar` on a bar,
|
|
7
|
+
// `.linked` around a button group. Twenty-one of those classes had NO rule in
|
|
8
|
+
// this package (#1049) — and two of them looked ported because their names
|
|
9
|
+
// appear in comments (`.osd`, `.linked`) while the code spells its own
|
|
10
|
+
// `.adw-linked`. A consumer following the upstream documentation got nothing,
|
|
11
|
+
// silently, which is the worst failure mode for a styling contract.
|
|
12
|
+
//
|
|
13
|
+
// Widget-scoped classes are NOT here — they live with the widget whose markup
|
|
14
|
+
// they modify (`.property` in `_action_row.scss`, `.compact` in
|
|
15
|
+
// `_status_page.scss`, `.devel` in `_headerbar.scss`, `.osd`'s button and
|
|
16
|
+
// progress-bar variants in theirs), because that is where their selector needs
|
|
17
|
+
// to see the widget's inner nodes. `scripts/check-adwaita-style-classes.mjs`
|
|
18
|
+
// holds the ledger of which class lives where and fails when one has no rule at
|
|
19
|
+
// all.
|
|
20
|
+
//
|
|
21
|
+
// Reference: refs/libadwaita/doc/style-classes.md (the documented surface)
|
|
22
|
+
// Reference: refs/libadwaita/src/stylesheet/_common.scss:25-28,43-60,90-104
|
|
23
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_misc.scss:4-7,21-32,197-236
|
|
24
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_views.scss:1-18
|
|
25
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_lists.scss:520-600
|
|
26
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_toolbars.scss:91-102
|
|
27
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_linked.scss:1-45
|
|
28
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_scrolling.scss:150-166
|
|
29
|
+
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
30
|
+
// Modifications: Adapted for @gjsify/adwaita-web — GTK node names (`row`,
|
|
31
|
+
// `list`, `button`) become the custom-element tags and `.adw-*` structure
|
|
32
|
+
// classes this package renders, and GTK-only drawing (`image()`,
|
|
33
|
+
// `-gtk-icon-shadow`, `border-spacing` on a box) becomes its CSS equivalent.
|
|
34
|
+
|
|
35
|
+
/************
|
|
36
|
+
* Surfaces *
|
|
37
|
+
************/
|
|
38
|
+
|
|
39
|
+
// _common.scss:25-28 — an opaque window-coloured background on any node.
|
|
40
|
+
.background {
|
|
41
|
+
background-color: var(--window-bg-color);
|
|
42
|
+
color: var(--window-fg-color);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// _views.scss:1-18 — the content-area surface. `:disabled` and `:selected` are
|
|
46
|
+
// GTK pseudo-classes with no DOM counterpart; `[disabled]`/`[aria-selected]`
|
|
47
|
+
// are how the same states are spelled here, and both are what our own widgets
|
|
48
|
+
// already set.
|
|
49
|
+
.view {
|
|
50
|
+
background-color: var(--view-bg-color);
|
|
51
|
+
color: var(--view-fg-color);
|
|
52
|
+
|
|
53
|
+
&:disabled,
|
|
54
|
+
&[disabled] {
|
|
55
|
+
color: color-mix(in srgb, var(--view-fg-color) var(--disabled-opacity), transparent);
|
|
56
|
+
background-color: color-mix(in srgb, var(--window-bg-color) 60%, var(--view-bg-color));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&[aria-selected='true'] {
|
|
60
|
+
background-color: color-mix(in srgb, var(--accent-bg-color) 25%, transparent);
|
|
61
|
+
border-radius: var(--button-radius);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// _misc.scss:4-7 — the default border. `--border-color` is derived from
|
|
66
|
+
// `currentColor`, so this follows the foreground of whatever it is drawn on.
|
|
67
|
+
.frame {
|
|
68
|
+
border: 1px solid var(--border-color);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/*********
|
|
72
|
+
* Cards *
|
|
73
|
+
*********/
|
|
74
|
+
|
|
75
|
+
// _misc.scss:197-236. The ONE card recipe: `adw-card` and the preferences
|
|
76
|
+
// group's list box each carried their own copy of these five declarations
|
|
77
|
+
// before this placeholder existed.
|
|
78
|
+
%card {
|
|
79
|
+
background-color: var(--card-bg-color);
|
|
80
|
+
color: var(--card-fg-color);
|
|
81
|
+
border-radius: var(--card-radius);
|
|
82
|
+
box-shadow:
|
|
83
|
+
0 0 0 1px rgb(0 0 6 / 3%),
|
|
84
|
+
0 1px 3px 1px rgb(0 0 6 / 7%),
|
|
85
|
+
0 2px 6px 2px rgb(0 0 6 / 3%);
|
|
86
|
+
|
|
87
|
+
@media (prefers-contrast: more) {
|
|
88
|
+
box-shadow:
|
|
89
|
+
0 0 0 1px var(--border-color),
|
|
90
|
+
0 1px 3px 1px rgb(0 0 6 / 7%),
|
|
91
|
+
0 2px 6px 2px rgb(0 0 6 / 3%);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.card {
|
|
96
|
+
@extend %card;
|
|
97
|
+
|
|
98
|
+
// `.card.activatable` gets the hover/active states of an activatable row
|
|
99
|
+
// (:225-235). GTK's `background-image: image(…)` is a solid-colour layer over
|
|
100
|
+
// the background; a one-stop gradient is its CSS equivalent, and it composes
|
|
101
|
+
// with `background-color` the same way — so a re-themed card keeps its colour
|
|
102
|
+
// and only gains the wash.
|
|
103
|
+
&.activatable {
|
|
104
|
+
transition:
|
|
105
|
+
background-color 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
|
|
106
|
+
box-shadow 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
107
|
+
|
|
108
|
+
&:hover {
|
|
109
|
+
background-image: linear-gradient(
|
|
110
|
+
color-mix(in srgb, currentColor 3%, transparent),
|
|
111
|
+
color-mix(in srgb, currentColor 3%, transparent)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
&:active {
|
|
116
|
+
background-image: linear-gradient(
|
|
117
|
+
color-mix(in srgb, currentColor 8%, transparent),
|
|
118
|
+
color-mix(in srgb, currentColor 8%, transparent)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// A card inside (or itself) an OSD surface loses its own elevation and reads
|
|
124
|
+
// as a translucent inset instead (:212-220).
|
|
125
|
+
.osd &,
|
|
126
|
+
&.osd {
|
|
127
|
+
background-color: color-mix(in srgb, currentColor 10%, transparent);
|
|
128
|
+
color: rgb(255 255 255 / 90%);
|
|
129
|
+
box-shadow: none;
|
|
130
|
+
|
|
131
|
+
@media (prefers-contrast: more) {
|
|
132
|
+
box-shadow: 0 0 0 1px var(--border-color);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/***************
|
|
138
|
+
* Boxed lists *
|
|
139
|
+
***************/
|
|
140
|
+
|
|
141
|
+
// _lists.scss:520-556. In GTK the row separators are drawn by the rows
|
|
142
|
+
// themselves and the list only supplies the card box plus the rounding of its
|
|
143
|
+
// two ends — which is exactly how this package's rows behave already
|
|
144
|
+
// (`adw-action-row` and friends draw their own `border-bottom` and drop it on
|
|
145
|
+
// `:last-child`), so the class is the box and the clip.
|
|
146
|
+
.boxed-list {
|
|
147
|
+
@extend %card;
|
|
148
|
+
|
|
149
|
+
display: flex;
|
|
150
|
+
flex-direction: column;
|
|
151
|
+
// The rounding of the first and last row is a CLIP here rather than per-row
|
|
152
|
+
// corner radii: a row is a custom element whose own background paints over
|
|
153
|
+
// the list's corners, and every row type would otherwise need the same four
|
|
154
|
+
// radii repeated.
|
|
155
|
+
overflow: hidden;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// :556-600 — same content, each row its own card. The list drops its box, and
|
|
159
|
+
// the rows carry it, spaced 12px apart.
|
|
160
|
+
.boxed-list-separate {
|
|
161
|
+
display: flex;
|
|
162
|
+
flex-direction: column;
|
|
163
|
+
gap: 12px;
|
|
164
|
+
background: none;
|
|
165
|
+
color: var(--card-fg-color);
|
|
166
|
+
|
|
167
|
+
> * {
|
|
168
|
+
@extend %card;
|
|
169
|
+
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
// A row that draws a separator for its neighbours must not draw one when it
|
|
172
|
+
// IS the card (:585-586: `border: none`).
|
|
173
|
+
border-bottom: none;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/************
|
|
178
|
+
* Toolbars *
|
|
179
|
+
************/
|
|
180
|
+
|
|
181
|
+
// _toolbars.scss:91-102 + doc "Toolbars". GTK's `border-spacing: 6px` on a box
|
|
182
|
+
// is the gap between children; the flex row is what makes that expressible.
|
|
183
|
+
//
|
|
184
|
+
// The automatic flat appearance follows upstream's rule list exactly: icon-only
|
|
185
|
+
// buttons, buttons with an `adw-button-content` (icon + label), menu buttons and
|
|
186
|
+
// split buttons go flat — unless they are `.raised`, `.suggested-action`,
|
|
187
|
+
// `.destructive-action`, `.opaque`, or sit inside a `.linked` group. Text-only
|
|
188
|
+
// buttons keep their frame, which is why this cannot be a blanket
|
|
189
|
+
// `.toolbar adw-button { … }`.
|
|
190
|
+
$_keeps-frame: ':not(.raised):not(.suggested-action):not(.destructive-action):not(.opaque)';
|
|
191
|
+
|
|
192
|
+
.toolbar {
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: 6px;
|
|
196
|
+
padding: 6px;
|
|
197
|
+
|
|
198
|
+
adw-button#{$_keeps-frame} .adw-button.icon-only,
|
|
199
|
+
adw-button#{$_keeps-frame}:has(adw-button-content) .adw-button,
|
|
200
|
+
adw-menu-button#{$_keeps-frame} .adw-button,
|
|
201
|
+
adw-split-button#{$_keeps-frame} .adw-button {
|
|
202
|
+
background-color: transparent;
|
|
203
|
+
|
|
204
|
+
&:hover {
|
|
205
|
+
background-color: var(--button-hover-color);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
&:active {
|
|
209
|
+
background-color: var(--button-active-color);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Inside a linked group the frames come BACK — upstream states this as its own
|
|
214
|
+
// rule (`_toolbars.scss:20-26`) rather than as an exclusion, because a linked
|
|
215
|
+
// group only reads as one control if its members are raised.
|
|
216
|
+
.linked,
|
|
217
|
+
.adw-linked {
|
|
218
|
+
adw-button#{$_keeps-frame} .adw-button,
|
|
219
|
+
adw-menu-button#{$_keeps-frame} .adw-button,
|
|
220
|
+
adw-split-button#{$_keeps-frame} .adw-button {
|
|
221
|
+
background-color: var(--button-bg-color);
|
|
222
|
+
|
|
223
|
+
&:hover {
|
|
224
|
+
background-color: var(--button-hover-color);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
&:active {
|
|
228
|
+
background-color: var(--button-active-color);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Stand-alone OSD toolbars — the floating video-player-controls shape
|
|
234
|
+
// (:99-102).
|
|
235
|
+
&.osd {
|
|
236
|
+
padding: 12px;
|
|
237
|
+
border-radius: calc(var(--button-radius) + 6px);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// _misc.scss:21-32 — a separator that reserves whitespace instead of drawing a
|
|
242
|
+
// line, for separating groups inside a toolbar.
|
|
243
|
+
.spacer {
|
|
244
|
+
background: none;
|
|
245
|
+
border: none;
|
|
246
|
+
flex-shrink: 0;
|
|
247
|
+
|
|
248
|
+
&,
|
|
249
|
+
&.horizontal {
|
|
250
|
+
min-width: 12px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
&.vertical {
|
|
254
|
+
min-height: 12px;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/*******
|
|
259
|
+
* OSD *
|
|
260
|
+
*******/
|
|
261
|
+
|
|
262
|
+
// _common.scss:43-60. The exclusions are upstream's: a progress bar, button,
|
|
263
|
+
// menu button, split button and inline view switcher each define what `.osd`
|
|
264
|
+
// means for them, and must not get the dark surface treatment on top.
|
|
265
|
+
//
|
|
266
|
+
// This is a token REBIND, not just a colour: everything accent-coloured inside
|
|
267
|
+
// an OSD surface turns white without knowing it is inside one.
|
|
268
|
+
.osd:not(adw-progress-bar):not(adw-button):not(adw-menu-button):not(adw-split-button):not(
|
|
269
|
+
adw-inline-view-switcher
|
|
270
|
+
) {
|
|
271
|
+
--accent-bg-color: rgb(255 255 255 / 75%);
|
|
272
|
+
--accent-fg-color: rgb(0 0 0 / 75%);
|
|
273
|
+
--accent-color: oklab(from var(--accent-bg-color) max(l, 0.85) a b);
|
|
274
|
+
--destructive-color: oklab(from var(--destructive-bg-color) max(l, 0.85) a b);
|
|
275
|
+
--success-color: oklab(from var(--success-bg-color) max(l, 0.85) a b);
|
|
276
|
+
--warning-color: oklab(from var(--warning-bg-color) max(l, 0.85) a b);
|
|
277
|
+
--error-color: oklab(from var(--error-bg-color) max(l, 0.85) a b);
|
|
278
|
+
|
|
279
|
+
color: rgb(255 255 255 / 90%);
|
|
280
|
+
border: none;
|
|
281
|
+
background-color: rgb(0 0 0 / 70%);
|
|
282
|
+
background-clip: padding-box;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/*******************
|
|
286
|
+
* Linked controls *
|
|
287
|
+
*******************/
|
|
288
|
+
|
|
289
|
+
// _linked.scss:1-45 + doc "Linked Controls": the children of a linked box share
|
|
290
|
+
// their touching edges. Upstream squares the inner corners and overlaps the
|
|
291
|
+
// borders by -1px; this package's controls have no border, so the overlap is
|
|
292
|
+
// dropped and only the corner squaring remains.
|
|
293
|
+
//
|
|
294
|
+
// `.adw-linked` is this package's own earlier spelling, kept as an alias — and
|
|
295
|
+
// it USED to add a 2px gap between children, which is a segmented look, not a
|
|
296
|
+
// linked one. The gap is gone; a consumer who wanted it can say `gap`.
|
|
297
|
+
// The styled node is either the child itself (a bare `.adw-button` /
|
|
298
|
+
// `.adw-entry`) or one rendered inside the child host element (`<adw-button>`,
|
|
299
|
+
// `<adw-menu-button>`, `<adw-drop-down>`, …) — so both spellings are matched
|
|
300
|
+
// rather than enumerating host tags the way GTK has to.
|
|
301
|
+
$_linked-nodes: '.adw-button', '.adw-entry';
|
|
302
|
+
|
|
303
|
+
.linked,
|
|
304
|
+
.adw-linked {
|
|
305
|
+
display: inline-flex;
|
|
306
|
+
|
|
307
|
+
&:not(.vertical) > * {
|
|
308
|
+
@each $node in $_linked-nodes {
|
|
309
|
+
&:not(:first-child) #{$node},
|
|
310
|
+
&:not(:first-child)#{$node} {
|
|
311
|
+
border-start-start-radius: 0;
|
|
312
|
+
border-end-start-radius: 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
&:not(:last-child) #{$node},
|
|
316
|
+
&:not(:last-child)#{$node} {
|
|
317
|
+
border-start-end-radius: 0;
|
|
318
|
+
border-end-end-radius: 0;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
&.vertical {
|
|
324
|
+
flex-direction: column;
|
|
325
|
+
|
|
326
|
+
> * {
|
|
327
|
+
@each $node in $_linked-nodes {
|
|
328
|
+
&:not(:first-child) #{$node},
|
|
329
|
+
&:not(:first-child)#{$node} {
|
|
330
|
+
border-start-start-radius: 0;
|
|
331
|
+
border-start-end-radius: 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
&:not(:last-child) #{$node},
|
|
335
|
+
&:not(:last-child)#{$node} {
|
|
336
|
+
border-end-start-radius: 0;
|
|
337
|
+
border-end-end-radius: 0;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/*************
|
|
345
|
+
* App icons *
|
|
346
|
+
*************/
|
|
347
|
+
|
|
348
|
+
// _common.scss:90-104. `-gtk-icon-shadow` has no CSS counterpart; `filter:
|
|
349
|
+
// drop-shadow()` is the equivalent that follows the icon's alpha rather than its
|
|
350
|
+
// box, which is what makes these legible on a light background. Each upstream
|
|
351
|
+
// shadow layer becomes one `drop-shadow()`.
|
|
352
|
+
.lowres-icon {
|
|
353
|
+
filter: drop-shadow(0 -1px rgb(0 0 6 / 5%)) drop-shadow(1px 0 rgb(0 0 6 / 10%))
|
|
354
|
+
drop-shadow(0 1px rgb(0 0 6 / 30%)) drop-shadow(-1px 0 rgb(0 0 6 / 10%));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.icon-dropshadow {
|
|
358
|
+
filter: drop-shadow(0 1px 12px rgb(0 0 6 / 5%)) drop-shadow(0 -1px rgb(0 0 6 / 5%))
|
|
359
|
+
drop-shadow(1px 0 rgb(0 0 6 / 10%)) drop-shadow(0 1px rgb(0 0 6 / 30%))
|
|
360
|
+
drop-shadow(-1px 0 rgb(0 0 6 / 10%));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/************************
|
|
364
|
+
* Undershoot indicators *
|
|
365
|
+
************************/
|
|
366
|
+
|
|
367
|
+
// _scrolling.scss:150-166 — a shadow on the given edge of a scrolling box,
|
|
368
|
+
// telling the reader there is content past it. GTK draws them with an inset
|
|
369
|
+
// background; an inset `box-shadow` on the scroll container is the CSS
|
|
370
|
+
// equivalent and, unlike a `::before`, stays put while the content scrolls.
|
|
371
|
+
//
|
|
372
|
+
// `-start`/`-end` follow text direction, same as `margin-start`/`margin-end`,
|
|
373
|
+
// which `:dir()` expresses directly.
|
|
374
|
+
//
|
|
375
|
+
// `adw-toolbar-view` carries the same two class names for its OWN top/bottom
|
|
376
|
+
// indicators, drawn on its content node against the header shade
|
|
377
|
+
// (`_toolbar_view.scss:67-86`) — so it is excluded here rather than getting both
|
|
378
|
+
// a host shadow and a content gradient.
|
|
379
|
+
$undershoot-color: color-mix(in srgb, currentColor 6%, transparent);
|
|
380
|
+
|
|
381
|
+
.undershoot-top:not(adw-toolbar-view) {
|
|
382
|
+
box-shadow: inset 0 6px 6px -6px $undershoot-color;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.undershoot-bottom:not(adw-toolbar-view) {
|
|
386
|
+
box-shadow: inset 0 -6px 6px -6px $undershoot-color;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.undershoot-start {
|
|
390
|
+
&:dir(ltr) {
|
|
391
|
+
box-shadow: inset 6px 0 6px -6px $undershoot-color;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
&:dir(rtl) {
|
|
395
|
+
box-shadow: inset -6px 0 6px -6px $undershoot-color;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.undershoot-end {
|
|
400
|
+
&:dir(ltr) {
|
|
401
|
+
box-shadow: inset -6px 0 6px -6px $undershoot-color;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
&:dir(rtl) {
|
|
405
|
+
box-shadow: inset 6px 0 6px -6px $undershoot-color;
|
|
406
|
+
}
|
|
407
|
+
}
|
package/scss/_switch.scss
CHANGED
|
@@ -79,10 +79,11 @@
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// libadwaita dims the whole widget (`filter: Opacity(var(--disabled-opacity))`,
|
|
82
|
-
// _switch.scss:20-22)
|
|
83
|
-
//
|
|
82
|
+
// _switch.scss:20-22), and `--disabled-opacity` is a real token here now — it
|
|
83
|
+
// used to be stood in for by `--dim-opacity`, which is a different number and
|
|
84
|
+
// moves the OTHER way under high contrast.
|
|
84
85
|
.adw-switch.disabled {
|
|
85
|
-
opacity: var(--
|
|
86
|
+
opacity: var(--disabled-opacity);
|
|
86
87
|
|
|
87
88
|
.adw-switch-slider {
|
|
88
89
|
cursor: not-allowed;
|
package/scss/_variables.scss
CHANGED
|
@@ -117,6 +117,21 @@
|
|
|
117
117
|
--font-size-heading: 12pt;
|
|
118
118
|
--dim-opacity: 0.55;
|
|
119
119
|
|
|
120
|
+
// Borders and disabled dimming, as libadwaita's own two-step (_colors.scss:263,
|
|
121
|
+
// 317-329): an OPACITY token the high-contrast query moves, and a colour
|
|
122
|
+
// DERIVED from it against `currentColor` — so a border follows the text colour
|
|
123
|
+
// of the surface it is drawn on instead of being a fixed grey.
|
|
124
|
+
//
|
|
125
|
+
// `--disabled-opacity` had no home here, and three partials said so in a
|
|
126
|
+
// comment and reached for `--dim-opacity` instead (`_switch.scss:81-83`,
|
|
127
|
+
// `_checks.scss:164-166`, `_progress_bar.scss:87-89`). Those are different
|
|
128
|
+
// numbers upstream — 50% disabled vs 55% dimmed — and under high contrast they
|
|
129
|
+
// move in OPPOSITE directions (disabled 40%, dimmed 90%), so the stand-in was
|
|
130
|
+
// most wrong exactly where it mattered most.
|
|
131
|
+
--border-opacity: 15%;
|
|
132
|
+
--border-color: color-mix(in srgb, currentColor var(--border-opacity), transparent);
|
|
133
|
+
--disabled-opacity: 0.5;
|
|
134
|
+
|
|
120
135
|
// Document + monospace families, behind `.document` / `.monospace`
|
|
121
136
|
// (_labels.scss:75-84). In GTK these four are written into `:root` at RUNTIME
|
|
122
137
|
// by AdwStyleManager from the desktop's font settings
|
|
@@ -134,3 +149,13 @@
|
|
|
134
149
|
--monospace-font-family: 'Adwaita Mono', ui-monospace, 'SF Mono', 'Menlo', monospace;
|
|
135
150
|
--monospace-font-size: var(--font-size-base);
|
|
136
151
|
}
|
|
152
|
+
|
|
153
|
+
// High contrast moves the three opacity tokens, and only those — every colour
|
|
154
|
+
// that reads them follows without its own query (_colors.scss:325-329).
|
|
155
|
+
@media (prefers-contrast: more) {
|
|
156
|
+
:root {
|
|
157
|
+
--border-opacity: 50%;
|
|
158
|
+
--dim-opacity: 0.9;
|
|
159
|
+
--disabled-opacity: 0.4;
|
|
160
|
+
}
|
|
161
|
+
}
|
package/scss/adwaita-skin.scss
CHANGED
|
@@ -25,6 +25,15 @@
|
|
|
25
25
|
// it (`_progress_bar.scss`'s text node, `_data_grid.scss`'s cells) exactly as
|
|
26
26
|
// libadwaita's own `_progress-bar.scss` does.
|
|
27
27
|
@use 'labels';
|
|
28
|
+
// The REST of libadwaita's documented style-class surface — the widget-
|
|
29
|
+
// independent half: the surfaces (`.background`, `.view`, `.frame`), `.card` and
|
|
30
|
+
// the two boxed lists, `.toolbar`, `.spacer`, `.osd`, `.linked`, the app-icon
|
|
31
|
+
// shadows and the undershoot indicators. Emitted here for the same two reasons
|
|
32
|
+
// as `_labels.scss`: a widget's own rule then wins on ordinary specificity, and
|
|
33
|
+
// `_card.scss` / `_preferences.scss` @extend `%card` from it.
|
|
34
|
+
// `scripts/check-adwaita-style-classes.mjs` holds the ledger of the whole
|
|
35
|
+
// documented surface and fails on a class with no rule behind it.
|
|
36
|
+
@use 'style_classes';
|
|
28
37
|
@use 'window';
|
|
29
38
|
@use 'headerbar';
|
|
30
39
|
@use 'window_title';
|
|
@@ -51,6 +60,9 @@
|
|
|
51
60
|
@use 'data_grid';
|
|
52
61
|
@use 'clamp';
|
|
53
62
|
@use 'spinner';
|
|
63
|
+
// <adw-shortcut-label>'s keycaps. After `labels`, because a separator inside it
|
|
64
|
+
// is an ordinary `.dimmed`.
|
|
65
|
+
@use 'shortcut_label';
|
|
54
66
|
@use 'avatar';
|
|
55
67
|
@use 'banner';
|
|
56
68
|
@use 'status_page';
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
// DOM-level conformance tests for <adw-about-dialog>, driven by the vectors in
|
|
2
2
|
// `@gjsify/adwaita-core/conformance` — the same table the core suite asserts.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// puts them (adw-about-dialog.ui:146-186), and `website` was treated as Details
|
|
9
|
-
// content, which is exactly the one input libadwaita leaves OUT of that
|
|
10
|
-
// predicate (:1108). Nothing compared the two, so nothing failed.
|
|
4
|
+
// A local copy of `parse_person`, of the page-visibility rules or of the fallbacks
|
|
5
|
+
// diverges from libadwaita in thirteen ways, two of them structural: Support Questions
|
|
6
|
+
// and Report an Issue belong on the MAIN page, not one navigation step deeper on Details;
|
|
7
|
+
// and `website` is the one input the Details-visibility predicate leaves OUT.
|
|
11
8
|
import { describe, expect, it } from '@gjsify/unit';
|
|
12
9
|
|
|
13
10
|
import { ADW_ABOUT_DIALOG_LABELS, stripMnemonic } from '@gjsify/adwaita-core';
|
|
@@ -33,9 +30,9 @@ interface Mounted {
|
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
/**
|
|
36
|
-
* Mount a dialog and set every attribute imperatively
|
|
37
|
-
*
|
|
38
|
-
*
|
|
33
|
+
* Mount a dialog and set every attribute imperatively: several vectors hinge on exact
|
|
34
|
+
* whitespace, which markup is not a reliable carrier for. `window.open` is stubbed
|
|
35
|
+
* because the link rows really do call it.
|
|
39
36
|
*/
|
|
40
37
|
function mount(attributes: Record<string, string> = {}): Mounted {
|
|
41
38
|
const host = document.createElement('div');
|
|
@@ -85,10 +82,8 @@ function rowTitles(scope: Element | null): string[] {
|
|
|
85
82
|
}
|
|
86
83
|
|
|
87
84
|
/**
|
|
88
|
-
* Activate a row and report the URI it asked for.
|
|
89
|
-
*
|
|
90
|
-
* The URI never reaches the DOM — it is captured in the row's activation
|
|
91
|
-
* handler — so the only way to observe it is to drive the row and listen.
|
|
85
|
+
* Activate a row and report the URI it asked for. The URI never reaches the DOM — it is
|
|
86
|
+
* captured in the row's activation handler — so driving the row is the only way to see it.
|
|
92
87
|
*/
|
|
93
88
|
function activateRow(dialog: HTMLElement, row: Element): string | null {
|
|
94
89
|
let uri: string | null = null;
|
|
@@ -121,8 +116,8 @@ export const AdwAboutDialogTest = async () => {
|
|
|
121
116
|
}
|
|
122
117
|
|
|
123
118
|
await it('makes a row with an empty link a LINK row, not a plain one', () => {
|
|
124
|
-
// `if (link)` is a
|
|
125
|
-
//
|
|
119
|
+
// `if (link)` is a POINTER test in the C, so `"Ada <>"` still links, to the
|
|
120
|
+
// URI `mailto:` — collapsing "" to null loses the row type.
|
|
126
121
|
const mounted = mount();
|
|
127
122
|
mounted.dialog.developers = ['Ada <>'];
|
|
128
123
|
const row = page(mounted.dialog, 'credits')!.querySelector('adw-action-row')!;
|
|
@@ -197,9 +192,9 @@ export const AdwAboutDialogTest = async () => {
|
|
|
197
192
|
}
|
|
198
193
|
|
|
199
194
|
await it('keeps a website-only dialog on the main page with no Details row', () => {
|
|
200
|
-
// `show_details` is `has_comments || has_custom_links
|
|
201
|
-
//
|
|
202
|
-
//
|
|
195
|
+
// `show_details` is `has_comments || has_custom_links`; website is
|
|
196
|
+
// deliberately not in it, so a dialog whose only extra is a website links
|
|
197
|
+
// straight from the main page.
|
|
203
198
|
const mounted = mount({ 'application-name': 'Builder', website: 'https://example.org' });
|
|
204
199
|
expect(page(mounted.dialog, 'details')).toBe(null);
|
|
205
200
|
expect(hasRow(page(mounted.dialog, 'main'), label('websiteRow'))).toBe(true);
|
|
@@ -208,8 +203,8 @@ export const AdwAboutDialogTest = async () => {
|
|
|
208
203
|
});
|
|
209
204
|
|
|
210
205
|
await describe('adw-about-dialog main page (update_support vectors)', async () => {
|
|
211
|
-
// debug-info has no counterpart
|
|
212
|
-
//
|
|
206
|
+
// debug-info has no counterpart here (no Troubleshooting page), so the core
|
|
207
|
+
// suite asserts those rows.
|
|
213
208
|
for (const vector of ABOUT_DIALOG_SUPPORT_VECTORS.filter((row) => !row.debugInfo)) {
|
|
214
209
|
const { supportUrl, issueUrl, visible, rule } = vector;
|
|
215
210
|
await it(`support=${supportUrl} issue=${issueUrl} — ${rule}`, () => {
|
|
@@ -235,8 +230,7 @@ export const AdwAboutDialogTest = async () => {
|
|
|
235
230
|
}
|
|
236
231
|
|
|
237
232
|
await it('puts the support and issue rows on the MAIN page, not on Details', () => {
|
|
238
|
-
// adw-about-dialog.ui
|
|
239
|
-
// main page, and the class docs say so outright (:87-90).
|
|
233
|
+
// In `adw-about-dialog.ui`, `support_group` is a child of the MAIN page.
|
|
240
234
|
const mounted = mount({
|
|
241
235
|
'application-name': 'Builder',
|
|
242
236
|
'support-url': 'https://example.org/help',
|
|
@@ -304,9 +298,8 @@ export const AdwAboutDialogTest = async () => {
|
|
|
304
298
|
}
|
|
305
299
|
|
|
306
300
|
await it('titles the dialog "About", not "About <app name>"', () => {
|
|
307
|
-
// The main page's title is bound to AdwDialog:title, whose template
|
|
308
|
-
//
|
|
309
|
-
// in the header revealer instead (.ui:29-31).
|
|
301
|
+
// The main page's title is bound to AdwDialog:title, whose template default
|
|
302
|
+
// is the bare word; the application name lives in the header revealer.
|
|
310
303
|
const mounted = mount({ 'application-name': 'Builder' });
|
|
311
304
|
expect(page(mounted.dialog, 'main')!.getAttribute('title')).toBe('About');
|
|
312
305
|
mounted.dispose();
|
|
@@ -323,12 +316,11 @@ export const AdwAboutDialogTest = async () => {
|
|
|
323
316
|
});
|
|
324
317
|
|
|
325
318
|
await it('does NOT open when a handler cancels it', () => {
|
|
326
|
-
// `activate_link_cb` returns the accumulated handler result
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
// ran regardless.
|
|
319
|
+
// `activate_link_cb` returns the accumulated handler result (accumulator
|
|
320
|
+
// `g_signal_accumulator_true_handled`): a handler saying "handled"
|
|
321
|
+
// suppresses the default navigation, and `preventDefault()` is the web
|
|
322
|
+
// spelling of that — so the event must be cancelable, or `window.open` runs
|
|
323
|
+
// regardless.
|
|
332
324
|
const mounted = mount({ 'application-name': 'Builder', website: 'https://example.org' });
|
|
333
325
|
mounted.dialog.addEventListener('activate-link', (event) => event.preventDefault());
|
|
334
326
|
const row = page(mounted.dialog, 'main')!.querySelector(`adw-action-row[title="${label('websiteRow')}"]`)!;
|
|
@@ -348,19 +340,14 @@ export const AdwAboutDialogTest = async () => {
|
|
|
348
340
|
});
|
|
349
341
|
});
|
|
350
342
|
|
|
351
|
-
//
|
|
343
|
+
// An opened dialog has to OCCUPY SPACE, and no other test here can see that: they
|
|
344
|
+
// all read the built DOM, and all passed while the sheet measured 360×0.
|
|
352
345
|
//
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
// `adw-about-dialog.ui:7` declares: `content-width` 360 and no
|
|
359
|
-
// content-height, libadwaita taking the height from the content. The content
|
|
360
|
-
// could not supply one — the navigation pages were absolutely positioned, so
|
|
361
|
-
// they contributed nothing to the page stack, which contributed nothing to
|
|
362
|
-
// the view, which left the sheet at zero; and the pages, being `inset: 0` of
|
|
363
|
-
// a zero-height parent, were zero in turn.
|
|
346
|
+
// `adw-about-dialog.ui` declares `content-width` 360 and no content-height, so
|
|
347
|
+
// libadwaita takes the height from the content — which absolutely-positioned
|
|
348
|
+
// navigation pages cannot supply, since they contribute nothing to the page stack,
|
|
349
|
+
// which contributes nothing to the view, and the pages are then `inset: 0` of a
|
|
350
|
+
// zero-height parent.
|
|
364
351
|
await describe('adw-about-dialog — an opened dialog is actually on screen', async () => {
|
|
365
352
|
await it('gives its sheet a height derived from its content', () => {
|
|
366
353
|
const mounted = mount({ 'application-name': 'Adwaita Storybook', version: '0.11.0' });
|
|
@@ -368,11 +355,9 @@ export const AdwAboutDialogTest = async () => {
|
|
|
368
355
|
|
|
369
356
|
const sheet = mounted.dialog.querySelector('.adw-about-dialog-sheet') as HTMLElement;
|
|
370
357
|
const rect = sheet.getBoundingClientRect();
|
|
371
|
-
// HEIGHT only
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
// 342), so the assertion would report the harness rather than the
|
|
375
|
-
// widget. A test should assert the rule the bug broke.
|
|
358
|
+
// HEIGHT only: pinning the width needs `calc(100vw - 48px)` restated in JS,
|
|
359
|
+
// where `100vw` and `innerWidth` disagree by the scrollbar, so the assertion
|
|
360
|
+
// would report the harness rather than the widget.
|
|
376
361
|
expect(rect.height > 0).toBe(true);
|
|
377
362
|
// And not merely the header: the sheet has to be taller than the one
|
|
378
363
|
// child that had a size of its own all along, which is what made the
|