@gjsify/adwaita-web 0.36.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/dist/adwaita-web.css +504 -34
  2. package/dist/adwaita-web.css.map +1 -1
  3. package/lib/types/accent.d.ts +28 -0
  4. package/lib/types/adw-accent.spec.d.ts +2 -0
  5. package/lib/types/adw-shortcut-label.spec.d.ts +2 -0
  6. package/lib/types/breakpoints.d.ts +10 -12
  7. package/lib/types/elements/adw-about-dialog.d.ts +19 -36
  8. package/lib/types/elements/adw-action-row.d.ts +9 -12
  9. package/lib/types/elements/adw-alert-dialog.d.ts +0 -12
  10. package/lib/types/elements/adw-avatar.d.ts +1 -0
  11. package/lib/types/elements/adw-bottom-sheet.d.ts +7 -12
  12. package/lib/types/elements/adw-button-content.d.ts +3 -4
  13. package/lib/types/elements/adw-button-row.d.ts +1 -1
  14. package/lib/types/elements/adw-carousel.d.ts +28 -49
  15. package/lib/types/elements/adw-checks.d.ts +2 -8
  16. package/lib/types/elements/adw-combo-row.d.ts +6 -6
  17. package/lib/types/elements/adw-data-grid.d.ts +5 -10
  18. package/lib/types/elements/adw-drop-down.d.ts +3 -11
  19. package/lib/types/elements/adw-entry-row.d.ts +20 -23
  20. package/lib/types/elements/adw-icon.d.ts +2 -4
  21. package/lib/types/elements/adw-inline-view-switcher.d.ts +18 -31
  22. package/lib/types/elements/adw-navigation-split-view.d.ts +7 -12
  23. package/lib/types/elements/adw-navigation-view.d.ts +2 -10
  24. package/lib/types/elements/adw-overlay-split-view.d.ts +28 -52
  25. package/lib/types/elements/adw-password-entry-row.d.ts +4 -5
  26. package/lib/types/elements/adw-popover.d.ts +2 -7
  27. package/lib/types/elements/adw-preferences-dialog.d.ts +21 -34
  28. package/lib/types/elements/adw-preferences-group.d.ts +12 -14
  29. package/lib/types/elements/adw-progress-bar.d.ts +3 -4
  30. package/lib/types/elements/adw-shortcut-label.d.ts +20 -0
  31. package/lib/types/elements/adw-sidebar.d.ts +5 -10
  32. package/lib/types/elements/adw-spinner.d.ts +3 -5
  33. package/lib/types/elements/adw-split-button.d.ts +6 -16
  34. package/lib/types/elements/adw-switch-row.d.ts +3 -3
  35. package/lib/types/elements/adw-tab-view.d.ts +16 -26
  36. package/lib/types/elements/adw-toast-overlay.d.ts +8 -13
  37. package/lib/types/elements/adw-view-stack.d.ts +7 -17
  38. package/lib/types/elements/adw-view-switcher-bar.d.ts +4 -8
  39. package/lib/types/elements/adw-view-switcher.d.ts +18 -38
  40. package/lib/types/elements/adw-wrap-box.d.ts +7 -17
  41. package/lib/types/elements/view-switcher-dom.d.ts +10 -13
  42. package/lib/types/index.d.ts +3 -0
  43. package/lib/types/source-view/hex.d.ts +7 -13
  44. package/lib/types/style-classes.spec.d.ts +11 -0
  45. package/lib/types/styles.generated.d.ts +1 -1
  46. package/package.json +18 -7
  47. package/scss/_about_dialog.scss +3 -2
  48. package/scss/_action_row.scss +33 -0
  49. package/scss/_avatar.scss +12 -0
  50. package/scss/_button.scss +44 -22
  51. package/scss/_card.scss +11 -8
  52. package/scss/_checks.scss +2 -4
  53. package/scss/_dialog.scss +3 -2
  54. package/scss/_headerbar.scss +23 -0
  55. package/scss/_popover.scss +8 -4
  56. package/scss/_preferences.scss +7 -7
  57. package/scss/_progress_bar.scss +9 -6
  58. package/scss/_reset.scss +1 -1
  59. package/scss/_shortcut_label.scss +80 -0
  60. package/scss/_sidebar.scss +57 -0
  61. package/scss/_status_page.scss +20 -0
  62. package/scss/_style_classes.scss +407 -0
  63. package/scss/_switch.scss +4 -3
  64. package/scss/_theme.scss +20 -2
  65. package/scss/_variables.scss +34 -1
  66. package/scss/adwaita-skin.scss +12 -0
  67. package/src/accent.ts +74 -0
  68. package/src/adw-about-dialog.spec.ts +34 -49
  69. package/src/adw-accent.spec.ts +105 -0
  70. package/src/adw-action-rows.spec.ts +25 -33
  71. package/src/adw-avatar.spec.ts +51 -20
  72. package/src/adw-banner.spec.ts +5 -8
  73. package/src/adw-button-content.spec.ts +9 -12
  74. package/src/adw-button-row.spec.ts +2 -4
  75. package/src/adw-carousel.spec.ts +40 -53
  76. package/src/adw-checks.spec.ts +36 -47
  77. package/src/adw-drop-down.spec.ts +25 -32
  78. package/src/adw-header-bar.spec.ts +5 -7
  79. package/src/adw-icon.spec.ts +23 -32
  80. package/src/adw-progress-bar.spec.ts +10 -12
  81. package/src/adw-row-state.spec.ts +9 -10
  82. package/src/adw-shortcut-label.spec.ts +112 -0
  83. package/src/adw-sidebar.spec.ts +17 -24
  84. package/src/adw-switch.spec.ts +11 -17
  85. package/src/adw-tab-view.spec.ts +7 -10
  86. package/src/adw-toast-overlay.spec.ts +7 -11
  87. package/src/adw-view-switcher-bar.spec.ts +10 -14
  88. package/src/adw-wrap-box.spec.ts +25 -36
  89. package/src/bottom-sheet.spec.ts +30 -36
  90. package/src/breakpoints.ts +15 -18
  91. package/src/chrome.spec.ts +13 -21
  92. package/src/elements/adw-about-dialog.ts +71 -114
  93. package/src/elements/adw-action-row.ts +22 -37
  94. package/src/elements/adw-alert-dialog.ts +25 -50
  95. package/src/elements/adw-avatar.ts +40 -15
  96. package/src/elements/adw-banner.ts +14 -18
  97. package/src/elements/adw-bottom-sheet.ts +31 -52
  98. package/src/elements/adw-button-content.ts +30 -37
  99. package/src/elements/adw-button-row.ts +14 -24
  100. package/src/elements/adw-carousel.ts +87 -148
  101. package/src/elements/adw-checks.ts +43 -56
  102. package/src/elements/adw-clamp.ts +8 -9
  103. package/src/elements/adw-combo-row.ts +17 -25
  104. package/src/elements/adw-data-grid.ts +26 -38
  105. package/src/elements/adw-dialog.ts +25 -56
  106. package/src/elements/adw-drop-down.ts +31 -51
  107. package/src/elements/adw-entry-row.ts +39 -46
  108. package/src/elements/adw-expander-row.ts +30 -53
  109. package/src/elements/adw-icon.ts +30 -53
  110. package/src/elements/adw-inline-view-switcher.ts +38 -67
  111. package/src/elements/adw-menu-button.ts +28 -58
  112. package/src/elements/adw-navigation-split-view.ts +35 -47
  113. package/src/elements/adw-navigation-view.ts +12 -28
  114. package/src/elements/adw-overlay-split-view.ts +100 -175
  115. package/src/elements/adw-password-entry-row.ts +18 -28
  116. package/src/elements/adw-popover.ts +28 -44
  117. package/src/elements/adw-preferences-dialog.ts +41 -66
  118. package/src/elements/adw-preferences-group.ts +33 -46
  119. package/src/elements/adw-progress-bar.ts +27 -47
  120. package/src/elements/adw-shortcut-label.ts +138 -0
  121. package/src/elements/adw-sidebar.ts +31 -39
  122. package/src/elements/adw-spin-row.ts +7 -14
  123. package/src/elements/adw-spinner.ts +31 -43
  124. package/src/elements/adw-split-button.ts +45 -75
  125. package/src/elements/adw-switch-row.ts +29 -46
  126. package/src/elements/adw-switch.ts +28 -47
  127. package/src/elements/adw-tab-view.ts +62 -103
  128. package/src/elements/adw-toast-overlay.ts +28 -35
  129. package/src/elements/adw-toggle-group.ts +9 -16
  130. package/src/elements/adw-view-stack.ts +32 -51
  131. package/src/elements/adw-view-switcher-bar.ts +26 -50
  132. package/src/elements/adw-view-switcher.ts +60 -110
  133. package/src/elements/adw-window-title.ts +4 -4
  134. package/src/elements/adw-wrap-box.ts +33 -59
  135. package/src/elements/view-switcher-dom.ts +15 -22
  136. package/src/entry-rows.spec.ts +18 -24
  137. package/src/index.ts +24 -15
  138. package/src/navigation-view.spec.ts +7 -10
  139. package/src/preferences-search.ts +11 -15
  140. package/src/preferences.spec.ts +13 -15
  141. package/src/source-view/adw-source-view.ts +6 -19
  142. package/src/source-view/hex.ts +7 -13
  143. package/src/split-button.spec.ts +32 -44
  144. package/src/split-views.spec.ts +83 -149
  145. package/src/style-classes.spec.ts +181 -0
  146. package/src/style-isolation.spec.ts +3 -6
  147. package/src/styles.generated.ts +1 -1
  148. package/src/tab-view.spec.ts +26 -35
  149. package/src/test.browser.mts +6 -0
  150. package/src/view-stack.spec.ts +23 -34
  151. 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). This package has no `--disabled-opacity` token, so the
83
- // existing `--dim-opacity` stands in rather than a fourth invented colour.
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(--dim-opacity);
86
+ opacity: var(--disabled-opacity);
86
87
 
87
88
  .adw-switch-slider {
88
89
  cursor: not-allowed;
package/scss/_theme.scss CHANGED
@@ -4,6 +4,10 @@
4
4
  // Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
5
5
  // Modifications: Adapted for @gjsify/adwaita-web Web Components.
6
6
 
7
+ // `light-theme` lives in _variables.scss; @use makes it reachable (the skin uses
8
+ // the module system, so a mixin there is namespaced rather than global).
9
+ @use 'variables';
10
+
7
11
  @mixin dark-theme {
8
12
  --window-bg-color: #222226;
9
13
  --window-fg-color: #ffffff;
@@ -64,7 +68,21 @@
64
68
  }
65
69
  }
66
70
 
67
- // Manual override
68
- :root.theme-dark {
71
+ // Manual override, on ANY element rather than only on `:root`.
72
+ //
73
+ // It was `:root.theme-dark`, which silently does nothing unless the class lands on
74
+ // `<html>` — so an EMBEDDED consumer could not give its own subtree a scheme without
75
+ // re-theming the host page around it. The storybook is exactly that case: it scopes
76
+ // to its own container, the class was present, the tokens never changed, and nothing
77
+ // reported a problem. Custom properties resolve per element, so an element-level
78
+ // selector overrides the inherited values for its subtree and for nothing else.
79
+ .theme-dark {
69
80
  @include dark-theme;
70
81
  }
82
+
83
+ // The light counterpart, for the same reason: on a dark system a subtree has to be
84
+ // able to ask for light, which `:root:not(.theme-light)` on the media query above
85
+ // cannot give it once the class sits anywhere but the root.
86
+ .theme-light {
87
+ @include variables.light-theme;
88
+ }
@@ -5,7 +5,7 @@
5
5
  // Copyright (c) 2025 csm (adwaita-web). MIT License.
6
6
  // Modifications: Subset adapted for @gjsify/adwaita-web Web Components.
7
7
 
8
- :root {
8
+ @mixin light-theme {
9
9
  // Window
10
10
  --window-bg-color: #fafafb;
11
11
  --window-fg-color: rgba(0, 0, 6, 0.8);
@@ -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,21 @@
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
+ // The tokens above are a MIXIN rather than a bare `:root` block so `.theme-light`
154
+ // can include them too (see `_theme.scss`). A light opt-out that exists only as
155
+ // `:not(.theme-light)` on the media query cannot serve a SUBTREE: on a dark system
156
+ // the root stays dark, and a container asking for light matches no rule at all.
157
+ :root {
158
+ @include light-theme;
159
+ }
160
+
161
+ // High contrast moves the three opacity tokens, and only those — every colour
162
+ // that reads them follows without its own query (_colors.scss:325-329).
163
+ @media (prefers-contrast: more) {
164
+ :root {
165
+ --border-opacity: 50%;
166
+ --dim-opacity: 0.9;
167
+ --disabled-opacity: 0.4;
168
+ }
169
+ }
@@ -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';
package/src/accent.ts ADDED
@@ -0,0 +1,74 @@
1
+ // Applying an Adwaita accent in the browser — the renderer half.
2
+ //
3
+ // The palette and the OkLab standalone derivation are `@gjsify/adwaita-core`'s
4
+ // (ADR 0004). This sets the two custom properties every accent-coloured rule in
5
+ // the stylesheet already reads:
6
+ //
7
+ // --accent-bg-color the fill, with white on top
8
+ // --accent-color the standalone colour, for text on the window
9
+ //
10
+ // BOTH ARE SET EXPLICITLY, from core's arithmetic, rather than setting only the
11
+ // background and letting CSS derive the other with
12
+ // `oklab(from var(--accent-bg-color) min(l, 0.5) a b)` — which is what
13
+ // libadwaita's own stylesheet does (`_colors.scss:166-170`). Two reasons: core is
14
+ // then the single source of truth for a value three renderers have to agree on,
15
+ // and the relative-colour form needs channel keywords inside `min()`, which is
16
+ // newer than this package's baseline. The CSS route stays available to a consumer
17
+ // who wants it; nothing here depends on it.
18
+ //
19
+ // NOTE, and it is a pre-existing inconsistency rather than something introduced
20
+ // here: `_variables.scss:55` hardcodes `--accent-color: #1c71d8` for the default
21
+ // blue, while libadwaita derives `#0461be`. The semantic colours in the same file
22
+ // DO use the relative-colour expression. Applying an accent through this module
23
+ // therefore also corrects the standalone colour; leaving it unset keeps the
24
+ // legacy value.
25
+ //
26
+ // Reference: refs/libadwaita/src/stylesheet/_colors.scss:146-170
27
+ // Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
28
+
29
+ import { adwaitaAccentBgColor, adwaitaAccentColor, type AdwAccentColorName } from '@gjsify/adwaita-core';
30
+
31
+ /** The custom property carrying the accent fill. */
32
+ export const ACCENT_BG_PROPERTY = '--accent-bg-color';
33
+ /** The custom property carrying the standalone accent, for text on the window. */
34
+ export const ACCENT_PROPERTY = '--accent-color';
35
+
36
+ /** Where and how to apply the accent. */
37
+ export interface ApplyAccentOptions {
38
+ /** The element to set the properties on. Defaults to `document.documentElement`. */
39
+ readonly target?: HTMLElement;
40
+ /**
41
+ * Which standalone variant to use. Defaults to the target's RESOLVED scheme,
42
+ * because the standalone colour is the one thing about an accent that differs
43
+ * between light and dark — reading it beats assuming light.
44
+ */
45
+ readonly dark?: boolean;
46
+ }
47
+
48
+ /**
49
+ * Whether `element` is currently rendering dark.
50
+ *
51
+ * `.theme-dark` / `.theme-light` are the stylesheet's manual overrides and win
52
+ * over the media query, so an explicit class is checked first and
53
+ * `prefers-color-scheme` only decides when neither is present.
54
+ */
55
+ export function isAdwaitaDark(element: HTMLElement): boolean {
56
+ if (element.closest('.theme-dark')) return true;
57
+ if (element.closest('.theme-light')) return false;
58
+ return globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false;
59
+ }
60
+
61
+ /** Set the accent custom properties for `name`. */
62
+ export function applyAdwaitaAccent(name: AdwAccentColorName, options: ApplyAccentOptions = {}): void {
63
+ const target = options.target ?? document.documentElement;
64
+ const dark = options.dark ?? isAdwaitaDark(target);
65
+
66
+ target.style.setProperty(ACCENT_BG_PROPERTY, adwaitaAccentBgColor(name));
67
+ target.style.setProperty(ACCENT_PROPERTY, adwaitaAccentColor(name, dark));
68
+ }
69
+
70
+ /** Drop the properties, so the stylesheet's own values apply again. */
71
+ export function clearAdwaitaAccent(target: HTMLElement = document.documentElement): void {
72
+ target.style.removeProperty(ACCENT_BG_PROPERTY);
73
+ target.style.removeProperty(ACCENT_PROPERTY);
74
+ }