@gjsify/adwaita-web 0.51.1 → 0.53.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 (113) hide show
  1. package/README.md +34 -0
  2. package/dist/adwaita-web.css +259 -31
  3. package/dist/adwaita-web.css.map +1 -1
  4. package/lib/types/accent.d.ts +12 -2
  5. package/lib/types/adw-appearance.spec.d.ts +2 -0
  6. package/lib/types/appearance.d.ts +41 -0
  7. package/lib/types/blueprint-layout.spec.d.ts +1 -0
  8. package/lib/types/blueprint-markup.spec.d.ts +4 -0
  9. package/lib/types/blueprint-tree.spec.d.ts +1 -0
  10. package/lib/types/elements/adw-bottom-sheet.d.ts +31 -13
  11. package/lib/types/elements/adw-carousel.d.ts +5 -0
  12. package/lib/types/elements/adw-header-bar.d.ts +15 -0
  13. package/lib/types/elements/adw-inline-view-switcher.d.ts +1 -0
  14. package/lib/types/elements/adw-navigation-view.d.ts +1 -0
  15. package/lib/types/elements/adw-sidebar.d.ts +10 -0
  16. package/lib/types/elements/adw-tab-view.d.ts +1 -0
  17. package/lib/types/elements/adw-toggle-group.d.ts +11 -0
  18. package/lib/types/elements/gtk-action-bar.d.ts +19 -0
  19. package/lib/types/elements/gtk-box.d.ts +17 -0
  20. package/lib/types/elements/gtk-button.d.ts +2 -0
  21. package/lib/types/elements/gtk-label.d.ts +122 -0
  22. package/lib/types/font-stack.spec.d.ts +2 -0
  23. package/lib/types/gio-menu.spec.d.ts +1 -0
  24. package/lib/types/gtk-box.spec.d.ts +1 -0
  25. package/lib/types/gtk-label.spec.d.ts +1 -0
  26. package/lib/types/gtk-value-doors.spec.d.ts +1 -0
  27. package/lib/types/index.d.ts +6 -1
  28. package/lib/types/namespace/gio.d.ts +1 -0
  29. package/lib/types/namespace/gtk.d.ts +5 -0
  30. package/lib/types/shared-tree-builder.d.ts +64 -0
  31. package/lib/types/source-view/asm6502.d.ts +1 -20
  32. package/lib/types/source-view/index.d.ts +3 -2
  33. package/lib/types/source-view/theme.d.ts +1 -1
  34. package/lib/types/string-list-slot.d.ts +4 -0
  35. package/lib/types/styles.generated.d.ts +1 -1
  36. package/lib/types/tags.spec.d.ts +1 -0
  37. package/lib/types/value-lists.spec.d.ts +1 -0
  38. package/package.json +8 -6
  39. package/scss/_action_bar.scss +51 -0
  40. package/scss/_banner.scss +8 -1
  41. package/scss/_bottom_sheet.scss +57 -0
  42. package/scss/_box.scss +23 -0
  43. package/scss/_button.scss +29 -7
  44. package/scss/_button_content.scss +6 -1
  45. package/scss/_button_row.scss +5 -2
  46. package/scss/_carousel.scss +6 -0
  47. package/scss/_labels.scss +126 -12
  48. package/scss/_navigation_split_view.scss +18 -0
  49. package/scss/_overlay_split_view.scss +5 -0
  50. package/scss/_reset.scss +29 -4
  51. package/scss/_split_button.scss +5 -2
  52. package/scss/_status_page.scss +31 -17
  53. package/scss/_theme.scss +11 -3
  54. package/scss/_toolbar_view.scss +10 -0
  55. package/scss/_variables.scss +23 -3
  56. package/scss/_widget.scss +55 -0
  57. package/scss/_window_title.scss +9 -0
  58. package/scss/adwaita-skin.scss +8 -2
  59. package/src/accent.ts +28 -5
  60. package/src/adw-appearance.spec.ts +178 -0
  61. package/src/adw-banner.spec.ts +16 -0
  62. package/src/adw-button-row.spec.ts +8 -0
  63. package/src/adw-header-bar.spec.ts +17 -0
  64. package/src/appearance.ts +207 -0
  65. package/src/blueprint-layout.spec.ts +223 -0
  66. package/src/blueprint-markup.spec.ts +155 -0
  67. package/src/blueprint-tree.spec.ts +174 -0
  68. package/src/bottom-sheet.spec.ts +166 -1
  69. package/src/chrome.spec.ts +5 -2
  70. package/src/elements/adw-bottom-sheet.ts +166 -59
  71. package/src/elements/adw-carousel.ts +10 -5
  72. package/src/elements/adw-clamp.ts +12 -0
  73. package/src/elements/adw-combo-row.ts +13 -4
  74. package/src/elements/adw-header-bar.ts +46 -3
  75. package/src/elements/adw-inline-view-switcher.ts +11 -0
  76. package/src/elements/adw-navigation-view.ts +10 -0
  77. package/src/elements/adw-sidebar.ts +22 -0
  78. package/src/elements/adw-spin-row.ts +44 -5
  79. package/src/elements/adw-status-page.ts +12 -6
  80. package/src/elements/adw-tab-view.ts +20 -3
  81. package/src/elements/adw-toggle-group.ts +45 -4
  82. package/src/elements/adw-toolbar-view.ts +9 -0
  83. package/src/elements/gtk-action-bar.ts +90 -0
  84. package/src/elements/gtk-box.ts +87 -0
  85. package/src/elements/gtk-button.ts +60 -13
  86. package/src/elements/gtk-drop-down.ts +10 -1
  87. package/src/elements/gtk-label.ts +415 -0
  88. package/src/elements/swipe-drag.ts +1 -1
  89. package/src/font-stack.spec.ts +110 -0
  90. package/src/gio-menu.spec.ts +125 -0
  91. package/src/globals.d.ts +7 -0
  92. package/src/gtk-box.spec.ts +168 -0
  93. package/src/gtk-button.spec.ts +123 -1
  94. package/src/gtk-label.spec.ts +595 -0
  95. package/src/gtk-value-doors.spec.ts +104 -0
  96. package/src/index.ts +32 -1
  97. package/src/keyboard-operable.spec.ts +5 -4
  98. package/src/namespace/gio.ts +19 -0
  99. package/src/namespace/gtk.ts +14 -0
  100. package/src/shared-tree-builder.ts +268 -0
  101. package/src/shared-trees.spec.ts +296 -32
  102. package/src/source-view/adw-source-view.ts +3 -3
  103. package/src/source-view/asm6502.ts +11 -191
  104. package/src/source-view/index.ts +17 -11
  105. package/src/source-view/theme.ts +23 -17
  106. package/src/split-button.spec.ts +7 -0
  107. package/src/string-list-slot.ts +21 -0
  108. package/src/styles.generated.ts +1 -1
  109. package/src/tags.spec.ts +51 -0
  110. package/src/test.browser.mts +25 -0
  111. package/src/value-lists.spec.ts +93 -0
  112. package/lib/types/source-view/hex.d.ts +0 -21
  113. package/src/source-view/hex.ts +0 -37
@@ -0,0 +1 @@
1
+ export declare const AdwTagsTest: () => Promise<void>;
@@ -0,0 +1 @@
1
+ export declare const AdwValueListsTest: () => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/adwaita-web",
3
- "version": "0.51.1",
3
+ "version": "0.53.0",
4
4
  "description": "Adwaita/Libadwaita web components for browser targets",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -45,14 +45,16 @@
45
45
  "@codemirror/language": "^6.12.4",
46
46
  "@codemirror/state": "^6.7.0",
47
47
  "@codemirror/view": "^6.43.4",
48
- "@gjsify/adwaita-core": "^0.51.1",
49
- "@gjsify/adwaita-fonts": "^0.51.1",
50
- "@gjsify/adwaita-icons": "^0.51.1",
48
+ "@gjsify/adwaita-core": "^0.53.0",
49
+ "@gjsify/adwaita-fonts": "^0.53.0",
50
+ "@gjsify/adwaita-icons": "^0.53.0",
51
51
  "@lezer/highlight": "^1.2.3"
52
52
  },
53
53
  "devDependencies": {
54
- "@gjsify/cli": "^0.51.1",
55
- "@gjsify/unit": "^0.51.1",
54
+ "@gjsify/blueprint": "^0.53.0",
55
+ "@gjsify/cli": "^0.53.0",
56
+ "@gjsify/unit": "^0.53.0",
57
+ "@gjsify/vite-plugin-blueprint": "^0.53.0",
56
58
  "sass": "^1.101.0",
57
59
  "typescript": "^6.0.3"
58
60
  },
@@ -0,0 +1,51 @@
1
+ // _action_bar.scss — <gtk-action-bar>, GTK's bottom bar of packed widgets.
2
+ //
3
+ // libadwaita styles the bar's inner box (`actionbar > revealer > box`); this element has
4
+ // no revealer, so the host carries that box's surface and the three sections are its
5
+ // start box, centre and end box. `border-spacing: 6px` there is the gap between the
6
+ // packed children here.
7
+ //
8
+ // `revealed` is applied by toggling the `hidden` attribute, and `[hidden]` is re-asserted
9
+ // as an AUTHOR rule for the reason `_view_switcher_bar.scss` gives: the UA rule loses to
10
+ // this block's own `display` by cascade origin.
11
+ //
12
+ // Reference: refs/libadwaita/src/stylesheet/widgets/_toolbars.scss (GtkActionBar)
13
+ // Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
14
+ // Modifications: Adapted for @gjsify/adwaita-web Web Components.
15
+
16
+ gtk-action-bar {
17
+ display: flex;
18
+ align-items: center;
19
+ gap: 6px;
20
+ box-sizing: border-box;
21
+ background-color: var(--headerbar-bg-color);
22
+ color: var(--headerbar-fg-color);
23
+ box-shadow: inset 0 1px var(--headerbar-shade-color);
24
+ padding: 7px 6px 6px 6px;
25
+
26
+ &[hidden] {
27
+ display: none;
28
+ }
29
+
30
+ > .adw-action-bar-start,
31
+ > .adw-action-bar-end {
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 6px;
35
+ }
36
+
37
+ > .adw-action-bar-start {
38
+ flex: 1 1 0;
39
+ justify-content: flex-start;
40
+ }
41
+
42
+ > .adw-action-bar-center {
43
+ display: flex;
44
+ align-items: center;
45
+ }
46
+
47
+ > .adw-action-bar-end {
48
+ flex: 1 1 0;
49
+ justify-content: flex-end;
50
+ }
51
+ }
package/scss/_banner.scss CHANGED
@@ -21,17 +21,24 @@ adw-banner {
21
21
  display: none;
22
22
  }
23
23
 
24
+ // The title is a `.heading` label and the button a plain GTK text button
25
+ // (adw-banner.ui:25, :29), so the title is bold and the button 34px tall with the 17px
26
+ // `.text-button` sides. Both were lighter here, and the banner stood 8px shorter than
27
+ // GTK's render of the gallery's banner `.blp`.
24
28
  .adw-banner-title {
25
29
  flex: 1 1 auto;
26
30
  min-width: 0;
27
31
  font-size: var(--font-size-base);
32
+ font-weight: 700;
28
33
  }
29
34
 
30
35
  .adw-banner-button {
31
36
  flex-shrink: 0;
37
+ box-sizing: border-box;
38
+ min-height: 34px;
32
39
  border: none;
33
40
  border-radius: var(--button-radius);
34
- padding: 4px 14px;
41
+ padding: 5px 17px;
35
42
  background: var(--button-bg-color);
36
43
  color: var(--window-fg-color);
37
44
  font-family: var(--font-family);
@@ -19,7 +19,11 @@ adw-bottom-sheet {
19
19
  color: var(--window-fg-color);
20
20
 
21
21
  // --- Persistent content layer ---
22
+ // A grid rather than a block, as GTK's content bin allocates its child the whole area and
23
+ // lets the child's `halign` / `valign` place it (`_widget.scss`); a block container
24
+ // resolves a vertical auto margin to zero.
22
25
  .adw-bottom-sheet-content {
26
+ display: grid;
23
27
  box-sizing: border-box;
24
28
  width: 100%;
25
29
  height: 100%;
@@ -100,11 +104,64 @@ adw-bottom-sheet {
100
104
  box-sizing: border-box;
101
105
  }
102
106
 
107
+ // --- Bottom bar (the collapsed form the sheet morphs out of) ---
108
+ // libadwaita: > sheet > stack > button — a flat, radius-less button filling the bar,
109
+ // with the sheet's top radius carried over, and `.inert` stripping only the feedback.
110
+ // It is a real button because upstream's bin is one (adw-bottom-sheet.c:1203).
111
+ .adw-bottom-sheet-bottom-bar {
112
+ display: block;
113
+ box-sizing: border-box;
114
+ width: 100%;
115
+ min-width: 0;
116
+ min-height: 0;
117
+ padding: 0;
118
+ border: none;
119
+ border-radius: 0;
120
+ font: inherit;
121
+ font-weight: inherit;
122
+ color: inherit;
123
+ background: none;
124
+ box-shadow: none;
125
+ cursor: pointer;
126
+
127
+ &:hover {
128
+ background-color: color-mix(in srgb, currentColor 5%, transparent);
129
+ }
130
+
131
+ &:active {
132
+ background-color: color-mix(in srgb, currentColor 10%, transparent);
133
+ }
134
+
135
+ // can-open off. The button stays enabled and focusable — only the feedback goes,
136
+ // because upstream refuses the click in the handler, not in the widget
137
+ // (adw-bottom-sheet.c:2033-2036).
138
+ &.inert {
139
+ cursor: default;
140
+
141
+ &:hover,
142
+ &:active {
143
+ background: none;
144
+ }
145
+ }
146
+ }
147
+
103
148
  // --- Open state ---
104
149
  &.open .adw-bottom-sheet-sheet {
105
150
  transform: translateY(0);
106
151
  }
107
152
 
153
+ // --- Bottom-bar state ---
154
+ // The sheet bin sits at rest with only the bar exposed. This transform is the port's
155
+ // `gtk_widget_set_child_visible` for the bin, so it is what `reveal-bottom-bar` takes
156
+ // away: no class here, and the bin slides off screen with the bar still inside it.
157
+ &.showing-bottom-bar .adw-bottom-sheet-sheet {
158
+ transform: translateY(0);
159
+ box-shadow:
160
+ 0 0 0 1px rgba(0, 0, 6, 0.06),
161
+ 0 2px 8px 2px rgba(0, 0, 6, 0.06),
162
+ 0 3px 20px 10px rgba(0, 0, 6, 0.05);
163
+ }
164
+
108
165
  // A non-modal sheet must not block the content beneath it.
109
166
  &:not(.modal) .adw-bottom-sheet-dimming {
110
167
  display: none;
package/scss/_box.scss ADDED
@@ -0,0 +1,23 @@
1
+ // _box.scss — <gtk-box>, GTK's one-axis container.
2
+ //
3
+ // libadwaita has no box partial: a box draws nothing, it only places children.
4
+ // The element sets `flex-direction` and `gap` inline from its normalised
5
+ // properties (gtk-box.ts); this is the rest of GtkBoxLayout's shape.
6
+ //
7
+ // `align-items: stretch`: a child's default `halign`/`valign` is FILL, so it takes
8
+ // the whole cross axis. `min-width`/`min-height: 0` are deliberately NOT set on
9
+ // the children: a GTK box never allocates a child less than its minimum, and a
10
+ // flex item's `auto` minimum is that.
11
+ //
12
+ // Reference: refs/gtk/gtk/gtkboxlayout.c (homogeneous: every child the same size)
13
+ // Copyright (c) The GTK Team. LGPLv2.1+.
14
+
15
+ gtk-box {
16
+ display: flex;
17
+ flex-direction: row;
18
+ align-items: stretch;
19
+ }
20
+
21
+ gtk-box[homogeneous] > * {
22
+ flex: 1 1 0;
23
+ }
package/scss/_button.scss CHANGED
@@ -80,20 +80,37 @@ gtk-button {
80
80
  }
81
81
  }
82
82
 
83
- // Destructive action — red.
83
+ // Destructive action — libadwaita's `%destructive_button` (_buttons.scss:183-210):
84
+ // red TEXT on a 15% tint of it, not a filled red button. It was filled here, so the
85
+ // gallery's "Delete" read as a second primary action beside the GTK render of the
86
+ // same `.blp`. The accent tokens are rebound as upstream does, so anything accent-
87
+ // coloured inside the button (a focus ring) turns red with it.
84
88
  &.destructive-action {
85
- background-color: var(--destructive-bg-color);
86
- color: var(--destructive-fg-color);
89
+ --accent-bg-color: var(--destructive-bg-color);
90
+ --accent-fg-color: var(--destructive-fg-color);
91
+ --accent-color: var(--destructive-color);
92
+
93
+ color: var(--destructive-color);
94
+ background-color: color-mix(in srgb, currentColor 15%, transparent);
87
95
 
88
96
  &:hover {
89
- filter: brightness(1.06);
97
+ background-color: color-mix(in srgb, currentColor 20%, transparent);
90
98
  }
91
99
 
92
100
  &:active {
93
- filter: brightness(0.9);
101
+ background-color: color-mix(in srgb, currentColor 35%, transparent);
94
102
  }
95
103
  }
96
104
 
105
+ // A label-only button — GTK's `.text-button`, which `gtk_button_set_label` puts on the
106
+ // button (_buttons.scss:72-75). 17px against the 14px every web button had, so each
107
+ // text button in the gallery's button row was 6px narrower than GTK's. Before the pill,
108
+ // circular and icon-only rules, which upstream also lets win.
109
+ &.text-button {
110
+ padding-left: 17px;
111
+ padding-right: 17px;
112
+ }
113
+
97
114
  // Icon-only square button (no label).
98
115
  &.icon-only {
99
116
  padding: 0;
@@ -107,9 +124,14 @@ gtk-button {
107
124
  border-radius: 9999px;
108
125
  }
109
126
 
110
- // Pill — fully-rounded ends.
127
+ // Pill — fully-rounded ends, and libadwaita's `%pill_button` padding
128
+ // (_buttons.scss: `padding: 10px 32px`). It was 6px 20px, which drew every pill
129
+ // 10px shorter and 24px narrower than GTK beside a GTK render of the same `.blp`.
130
+ // GTK's height is `min-height: 24px` plus that padding, so 44; the floor is
131
+ // restated because the base's 34 would otherwise hold the box at the text height.
111
132
  &.pill {
112
- padding: 6px 20px;
133
+ min-height: 44px;
134
+ padding: 10px 32px;
113
135
  border-radius: 9999px;
114
136
  }
115
137
 
@@ -23,7 +23,12 @@
23
23
  // below. It lives in this file rather than in `_button.scss` because the class
24
24
  // belongs to this widget, and `.adw-button.image-text-button` beats `.adw-button`
25
25
  // on specificity regardless of order.
26
- .adw-button.image-text-button {
26
+ //
27
+ // `:not(.pill)` because upstream states `.pill` (:322-323) AFTER this block at the same
28
+ // specificity, so a pill keeps its `10px 32px` whatever it holds. This file is emitted
29
+ // after `_button.scss`, so without the exclusion the order flipped: a pill button with
30
+ // button content was drawn 9px from its label, 47px narrower than GTK.
31
+ .adw-button.image-text-button:not(.pill) {
27
32
  padding-left: 9px;
28
33
  padding-right: 9px;
29
34
  }
@@ -12,8 +12,11 @@ adw-button-row {
12
12
  align-items: center;
13
13
  justify-content: center;
14
14
  width: 100%;
15
- min-height: 50px;
16
- padding: var(--spacing-xs) var(--spacing-m);
15
+ // libadwaita: `row.button > box { min-height: 40px; margin: 0 12px }` (_lists.scss:458-462),
16
+ // and the row around it measures 44 in a GTK render of the gallery's button-row `.blp`.
17
+ // It was a plain row's 50 here, so the button row stood 6px taller than GTK's.
18
+ min-height: 44px;
19
+ padding: 0 var(--spacing-m);
17
20
  border-bottom: 1px solid var(--card-shade-color);
18
21
  color: var(--card-fg-color);
19
22
  cursor: pointer;
@@ -20,6 +20,12 @@
20
20
  adw-carousel {
21
21
  display: block;
22
22
  overflow: hidden;
23
+ // A carousel's own width is its parent's to give, never its pages laid end to end. The
24
+ // track is a row of full-width pages, so its max-content width is their SUM, and a
25
+ // carousel in a stretching box (the gallery's `.blp`) pushed that box 274px past its
26
+ // window. GTK measures a carousel by its widest page; CSS has no "max of the children"
27
+ // for a scrolling row, so the width comes from outside, as a filled GTK allocation does.
28
+ contain: inline-size;
23
29
 
24
30
  .adw-carousel-track {
25
31
  display: flex;
package/scss/_labels.scss CHANGED
@@ -1,14 +1,14 @@
1
- // _labels.scss — libadwaita's typography + semantic-colour UTILITY CLASSES.
1
+ // _labels.scss — libadwaita's typography + semantic-colour UTILITY CLASSES, and
2
+ // the `label {}` rules of the element they are written for.
2
3
  //
3
- // NO ELEMENT. `refs/libadwaita/src/stylesheet/widgets/_labels.scss` is a widget
4
- // partial in name only: `label { … }` is four lines of GtkLabel plumbing
5
- // (`caret-color`, `.separator`, `:disabled`) and everything else in it is a
6
- // CLASS a consumer puts on whatever node it already has. There is deliberately
7
- // no `<adw-label>` here to go with them — it would wrap a `<span>` and add
8
- // nothing a class does not, which is exactly ADR 0004's "a widget with
9
- // genuinely trivial behaviour does not need a core class" applied one level up,
10
- // and ADR 0010 already makes the token/class surface the public contract. An
11
- // element would only add a tag to `$adw-components` for no behaviour.
4
+ // `refs/libadwaita/src/stylesheet/widgets/_labels.scss` is a widget partial in
5
+ // name only: `label { … }` is four lines of GtkLabel plumbing and everything else
6
+ // in it is a CLASS a consumer puts on whatever node it already has — a `<span>` on
7
+ // a hand-written page, or <gtk-label>, which exists because an AUTHORED tree (a
8
+ // Blueprint `Gtk.Label` mounted through `mountSharedTree`) arrives as a tag with a
9
+ // `label` attribute and needs an element to read it. ADR 0010 makes the class
10
+ // surface the public contract either way; the element adds the layout below and
11
+ // nothing to the classes.
12
12
  //
13
13
  // This is also where the in-tree duplication ends: `_data_grid.scss` declared
14
14
  // `font-variant-numeric: tabular-nums` twice and inlined a whole monospace font
@@ -41,8 +41,122 @@
41
41
  // Reference: refs/adwaita-web/adwaita-web/scss/_utility_classes.scss (the vendored port's variant, NOT followed — see above)
42
42
  // Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
43
43
  // Copyright (c) 2025 csm (adwaita-web). MIT License.
44
- // Modifications: Adapted for @gjsify/adwaita-web; the `label {}` element rules
45
- // are GtkLabel plumbing with no DOM counterpart and are not ported.
44
+ // Modifications: Adapted for @gjsify/adwaita-web; `label {}` is ported onto
45
+ // <gtk-label> without `:disabled`, which the element has no state for.
46
+
47
+ // <gtk-label>: `label {}` (_labels.scss:1-11) plus the layout GtkLabel does in C.
48
+ //
49
+ // THE TWO SPACERS ARE `xalign`. GTK places the text at `xalign * (width − text
50
+ // width)` inside the label's allocation (gtklabel.c, gtk_label_get_layout_location); the
51
+ // element's free space is split between `::before` and `::after` in the ratio
52
+ // `xalign : 1 − xalign`, which is that formula, and flips in RTL as the C does.
53
+ //
54
+ // `YALIGN` HAS NO CONTINUUM ON THE CROSS AXIS. `xalign`'s spacer trick splits free space
55
+ // along flexbox's MAIN axis, where a flex item's `flex-grow` ratio is a real number;
56
+ // `align-items` is the CROSS-axis equivalent and CSS gives it three keywords, not a ratio
57
+ // — there is no `align-items: 0.25` the way there is a `flex-grow: 0.25`. A second pair of
58
+ // spacers would need a second axis, and a `<gtk-label>` cannot be both a row (for `xalign`)
59
+ // and a column (for `yalign`) at once without an inner wrapper element, which would cost
60
+ // every single-line label the "one text node, no children" DOM shape `gtk-label.spec.ts`
61
+ // pins for the XSS answer. So `--gtk-label-align-items` is set by `labelYalignAlignItems`
62
+ // (`@gjsify/adwaita-core`) from the NEAREST of three zones — `flex-start` below `0.25`,
63
+ // `flex-end` above `0.75`, `center` between (the default, unchanged from before this
64
+ // property existed) — exact at the three values GTK's own default and both edges take,
65
+ // and a DECLARED divergence for anything else: nearest zone, not a silent half-split, and
66
+ // `label.spec.ts`/`gtk-label.spec.ts` both say so where they test the intermediate values.
67
+ //
68
+ // `white-space: pre`: Pango keeps a newline and a run of spaces, and a label that
69
+ // does not wrap is as wide as its text — which a flex item's `min-width: auto`
70
+ // already makes its minimum. `wrap` breaks at word boundaries, WORD being the
71
+ // default `wrap-mode` — so does `ellipsize` alone, see `.adw-label-clamp` below.
72
+ gtk-label {
73
+ display: flex;
74
+ align-items: var(--gtk-label-align-items, center);
75
+ white-space: pre;
76
+ caret-color: currentColor;
77
+ // GtkLabel is not selectable unless asked (`selectable` defaults to FALSE).
78
+ user-select: none;
79
+
80
+ &::before,
81
+ &::after {
82
+ content: '';
83
+ flex-shrink: 1;
84
+ flex-basis: 0;
85
+ }
86
+
87
+ &::before {
88
+ flex-grow: var(--gtk-label-xalign, 0.5);
89
+ }
90
+
91
+ &::after {
92
+ flex-grow: calc(1 - var(--gtk-label-xalign, 0.5));
93
+ }
94
+
95
+ &[wrap] {
96
+ white-space: pre-wrap;
97
+ }
98
+
99
+ // `gtk-label.ts`'s span-render path: the text moves into this span so it can be pinned
100
+ // to its measured wrap width (a flex item is blockified by spec, but `display: block` is
101
+ // spelled out rather than relied on implicitly). Present whenever `wrap` is set OR
102
+ // `ellipsize` is active — MEASURED (`@gjsify/adwaita-core`'s `label.ts` header): GTK's
103
+ // own Pango layout wraps onto multiple lines from `ellipsize` ALONE, `wrap` or not, so
104
+ // both cases need the same span and the same measurement pass.
105
+ .adw-label-text {
106
+ display: block;
107
+ // A pinned width, or a shrink-to-fit one, may never push the label wider than its
108
+ // container.
109
+ max-width: 100%;
110
+ }
111
+
112
+ // `wrap-mode` — Pango's break-opportunity choice — is a real `word-break`/
113
+ // `overflow-wrap` mapping, not a declared gap: CSS's `break-all` is Pango's CHAR (break
114
+ // anywhere), and `overflow-wrap: anywhere` is Pango's WORD_CHAR (word boundaries, falling
115
+ // back to a character break only when a single word will not fit) — the pspec's own
116
+ // words for that member. WORD, the default, is the browser's un-overridden behaviour, so
117
+ // it has no rule here. Scoped to `.adw-label-text` alone, not `[wrap]`: `wrap-mode` only
118
+ // matters once the layout can span more than one line at all, which `ellipsize` alone
119
+ // already grants (see above) — the span's mere existence is that condition.
120
+ &[wrap-mode='char'] .adw-label-text {
121
+ word-break: break-all;
122
+ }
123
+
124
+ &[wrap-mode='word-char'] .adw-label-text {
125
+ overflow-wrap: anywhere;
126
+ }
127
+
128
+ // `ellipsize` — a JS-toggled CLASS, not an attribute selector: `.adw-label-clamp` is set
129
+ // by `gtk-label.ts`'s `_renderSpan` from `ellipsize`'s NORMALIZED value, so a raw,
130
+ // unrecognised `ellipsize` attribute (which `normalizeLabelEllipsize` already reads as
131
+ // `none`) can never leave this rule half-applied the way an `[ellipsize]` PRESENCE
132
+ // selector would. `lines`, gated by `labelEffectiveLines` (`@gjsify/adwaita-core`) to
133
+ // "ellipsize is active", `wrap` or not — MEASURED, the same header — applies here
134
+ // REGARDLESS of `wrap`: a real `Gtk.Label` allocated with `wrap=FALSE, ellipsize=END,
135
+ // lines=2` lays out exactly two lines, ellipsized, not one. `-webkit-line-clamp` needs
136
+ // its own box context (`display: -webkit-box` + `-webkit-box-orient: vertical`) and text
137
+ // that can actually WRAP (`white-space: normal`, overriding the host's inherited `pre`/
138
+ // `pre-wrap`) to have more than one line to clamp in the first place; the default of `1`
139
+ // is Pango's own when `lines` is unset (`--gtk-label-lines` is still set then — see
140
+ // `_render()` — so the fallback here is only a defensive belt for a span whose host
141
+ // somehow reached this class without it).
142
+ .adw-label-text.adw-label-clamp {
143
+ display: -webkit-box;
144
+ -webkit-box-orient: vertical;
145
+ -webkit-line-clamp: var(--gtk-label-lines, 1);
146
+ overflow: hidden;
147
+ text-overflow: var(--gtk-label-ellipsize-overflow, ellipsis);
148
+ white-space: normal;
149
+ }
150
+
151
+ &[selectable] {
152
+ user-select: text;
153
+ cursor: text;
154
+ }
155
+
156
+ &.separator {
157
+ @extend .dimmed;
158
+ }
159
+ }
46
160
 
47
161
  // _labels.scss:13-15
48
162
  .dimmed {
@@ -49,6 +49,24 @@ adw-navigation-split-view {
49
49
  background-color: var(--window-bg-color);
50
50
  }
51
51
 
52
+ // A pane's page fills the pane, as GTK allocates it the whole pane. The two panes are
53
+ // flex columns and an `<adw-navigation-page>` is a block, so a page in one — which is
54
+ // what a `.blp` writes, `sidebar: Adw.NavigationPage { … }` — was sized by its content:
55
+ // the gallery's sidebar list stopped 118px short of the pane it sat in. Its child fills
56
+ // it in turn, the way `.adw-navigation-page > *` does inside a navigation view.
57
+ .adw-nsv-sidebar > adw-navigation-page,
58
+ .adw-nsv-content > adw-navigation-page {
59
+ display: flex;
60
+ flex-direction: column;
61
+ flex: 1 1 auto;
62
+ min-height: 0;
63
+
64
+ > * {
65
+ flex: 1 1 auto;
66
+ min-height: 0;
67
+ }
68
+ }
69
+
52
70
  // `sidebar-position: end` docks the sidebar past the content. flex `order`
53
71
  // flips the visual order without changing the slotting DOM order.
54
72
  &.sidebar-end {
@@ -32,7 +32,12 @@ adw-overlay-split-view {
32
32
  // of the two a pane is in is exactly what decides its colour — hard-coding
33
33
  // `--sidebar-bg-color` on this private class painted a collapsed overlay,
34
34
  // which libadwaita puts on the window background, as though it were docked.
35
+ // A flex column, as the content pane is, so the pane's widget fills it: GTK allocates the
36
+ // sidebar the whole pane. A block pane sized a toolbar view by its content, and the
37
+ // gallery's `.blp` sidebar list stopped 130px above the pane's bottom edge.
35
38
  .adw-osv-sidebar {
39
+ display: flex;
40
+ flex-direction: column;
36
41
  overflow-y: auto;
37
42
  flex-shrink: 0;
38
43
  z-index: 10;
package/scss/_reset.scss CHANGED
@@ -29,14 +29,14 @@
29
29
  // never listed, so it silently ran without the isolation floor. Nothing noticed,
30
30
  // because the only spec touching this file instantiates `adw-switch-row`.
31
31
  $adw-components:
32
- adw-about-dialog, adw-action-row, adw-alert-dialog, adw-alert-response,
33
- adw-avatar, adw-banner, adw-bottom-sheet, adw-bottom-sheet-content,
34
- adw-bottom-sheet-sheet, gtk-button, adw-button-content, adw-button-row,
32
+ adw-about-dialog, adw-action-row, gtk-action-bar, adw-alert-dialog, adw-alert-response,
33
+ adw-avatar, adw-banner, adw-bottom-sheet, adw-bottom-sheet-bottom-bar,
34
+ adw-bottom-sheet-content, adw-bottom-sheet-sheet, gtk-box, gtk-button, adw-button-content, adw-button-row,
35
35
  adw-card, adw-carousel, adw-carousel-indicator-dots,
36
36
  adw-carousel-indicator-lines, gtk-check-button, adw-clamp, adw-combo-row,
37
37
  adw-data-grid,
38
38
  adw-dialog, gtk-drop-down, gtk-entry, adw-entry-row, adw-expander-row,
39
- adw-header-bar, gtk-image, adw-inline-view-switcher, gtk-menu-button,
39
+ adw-header-bar, gtk-image, adw-inline-view-switcher, gtk-label, gtk-menu-button,
40
40
  adw-navigation-page,
41
41
  adw-navigation-split-view, adw-navigation-view, adw-overlay-split-view,
42
42
  adw-password-entry-row, gtk-popover, adw-preferences-dialog,
@@ -70,6 +70,31 @@ $adw-components:
70
70
  text-shadow: none;
71
71
  }
72
72
 
73
+ // A box and a label INHERIT inside another widget, as every GTK node does. They
74
+ // are the two elements an authored tree puts INSIDE the others — a label in a
75
+ // `.error` box, a box of labels in a header bar — and the floor above would
76
+ // repaint every one of them in `--window-fg-color` at the base size, whatever
77
+ // its parent says. Nested in any component the floor already stops the host
78
+ // page, so only the OUTERMOST one keeps it. `:where()` adds no specificity, so a
79
+ // class on the element itself (`.title-1`, `.dimmed`) still wins.
80
+ //
81
+ // A button's content inherits for the same reason: `<adw-button-content>` is the child
82
+ // of a `<gtk-button>`, and the floor painted its icon and label in the window colour on
83
+ // a `.suggested-action` button, where GTK draws them in the accent foreground.
84
+ :where(#{$adw-components}) :is(gtk-box, gtk-label, adw-button-content) {
85
+ color: inherit;
86
+ font-family: inherit;
87
+ font-size: inherit;
88
+ font-weight: inherit;
89
+ font-style: inherit;
90
+ font-variant: inherit;
91
+ line-height: inherit;
92
+ letter-spacing: inherit;
93
+ word-spacing: inherit;
94
+ text-transform: inherit;
95
+ text-shadow: inherit;
96
+ }
97
+
73
98
  // Descendants: `box-sizing` does not inherit, so a host `*, *::before {
74
99
  // box-sizing: content-box }` reset would still reach internal elements. Pin it
75
100
  // to `border-box` (adwaita's model throughout) inside every component.
@@ -79,9 +79,12 @@
79
79
  align-items: center;
80
80
  justify-content: center;
81
81
  min-height: 34px;
82
- width: 28px;
82
+ // libadwaita: `splitbutton > menubutton > button { padding: 0 4px }` over the button's
83
+ // 16px minimum (_buttons.scss:486-489), so the arrow half is 24px. It was 28, and the
84
+ // gallery's split button measured 5px wider than GTK's render of the same `.blp`.
85
+ width: 24px;
83
86
  margin: 0;
84
- padding: 0 var(--spacing-xs);
87
+ padding: 0 4px;
85
88
  border: none;
86
89
  border-left: 1px solid var(--separator-color);
87
90
  border-radius: 0 var(--button-radius) var(--button-radius) 0;