active_admin_theme 2.0.0 → 3.1.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.
@@ -5,13 +5,355 @@ $skinMainSecondColor: #5ea3d3!default;
5
5
  $skinBorderRadius: 4px!default;
6
6
  $skinBorderWindowColor: #e6e9ee!default;
7
7
  $skinTablePadding: 10px!default;
8
+
9
+ // Content palette — the surfaces, text and borders the UI is built from, per
10
+ // mode. Each has a light value (the existing look) and a *Dark value; they feed
11
+ // the --aa-* custom properties below. Defaults reproduce the current colors, so
12
+ // non-configuring projects are unchanged.
13
+ $skinPageBgColor: #f7f9fb!default; $skinPageBgColorDark: #161a1e!default; // page background
14
+ $skinSurfaceColor: #ffffff!default; $skinSurfaceColorDark: #25292f!default; // panels / cards / content
15
+ $skinSurface2Color: #f0f2f5!default; $skinSurface2ColorDark: #2c3137!default; // table headers / striping / subtle fills
16
+ $skinSurfaceHoverColor: #f5f7fa!default; $skinSurfaceHoverColorDark:#3f454d!default; // row / item hover
17
+ $skinSelectedRowColor: #d9e4ec!default; $skinSelectedRowColorDark: #304457!default; // checked table row
18
+ // Things that float above the page — tool-row buttons and dropdown panels.
19
+ // On light they match the content surface; on dark they have to sit a step
20
+ // lighter than the page or they read as holes in it.
21
+ $skinElevatedColor: $skinSurfaceColor!default; $skinElevatedColorDark: #363c43!default;
22
+ $skinTextColor: #323537!default; $skinTextColorDark: #dde2e8!default; // body text
23
+ $skinTextMutedColor: #6b7177!default; $skinTextMutedColorDark: #b0b8c2!default; // secondary text / axis labels
24
+ $skinBorderColor: #e0e4e9!default; $skinBorderColorDark: #404750!default; // borders / grid lines
25
+ $skinInputBgColor: #ffffff!default; $skinInputBgColorDark: #1e2227!default; // form control background
26
+ $skinInputBorderColor: #c9ced4!default; $skinInputBorderColorDark: #4d555f!default; // form control border
27
+
28
+ // Header menu colors. Each defaults to the previous value, so projects that do
29
+ // not set these compile to identical CSS (and a project that overrides
30
+ // $skinMainSecondColor still gets the menu following that accent).
31
+ $skinMenuPillColor: #2e3236!default; // top-level current/hover pill
32
+ $skinMenuTextColor: #dfe2e6!default; // dropdown item text (was: inherited #fff)
33
+ $skinMenuPillTextColor: #6cb0de!default; // text on that pill; follows the dropdown text so a
34
+ // light menu is one variable to set, not two
35
+ $skinMenuPanelColor: #2e3236!default; // dropdown panel bg + hover "bridge" border
36
+ $skinMenuItemHoverColor: #3f454c!default; // dropdown item hover/current bg (was: none)
37
+ $skinMenuItemHoverTextColor: #6cb0de!default; // hover/current dropdown item text, same reason
38
+
39
+ // Header / menu sizing. Defaults reproduce (or keep the same total as) the
40
+ // previous hard-coded values, so non-configuring projects are unchanged.
41
+ $skinMenuFontSize: 13px!default; // header menu text size
42
+ $skinMenuItemPaddingY: 5px!default; // dropdown item top/bottom padding (was 6px/4px + a 7px border)
43
+ $skinMenuPanelMaxWidth: 260px!default; // dropdown panel ceiling; longer labels wrap instead of leaving the viewport
44
+ $skinMenuItemLineHeight: 1.35!default; // dropdown item line-height
45
+ // Header padding. Set $skinHeaderPaddingY for the same value top and bottom,
46
+ // or the two halves separately — the halves win, since they are read last.
47
+ $skinHeaderPaddingY: null!default;
48
+ $skinHeaderPaddingTop: if($skinHeaderPaddingY == null, 4.5px, $skinHeaderPaddingY)!default;
49
+ $skinHeaderPaddingBottom: if($skinHeaderPaddingY == null, 4.5px, $skinHeaderPaddingY)!default;
50
+ $skinHeaderLogoMaxHeight: none!default; // cap the site_title logo image height
51
+
52
+ // Title bar
53
+ $skinTitleBarColor: #343c46!default;
54
+ $skinTitleBarBorderColor: $skinMainSecondColor!default;
55
+ $skinTitleBarBorderWidth: 0!default;
56
+ $skinTitleBarButtonPaddingY: 6px!default; // action button vertical padding
57
+ $skinTitleBarButtonPaddingX: 10px!default; // action button horizontal padding
58
+
59
+ // Panel/section headers + tabs accent (the blue bar on panel headers and the
60
+ // inactive tabs). $skinPanelHeaderColor is the base/light value;
61
+ // $skinPanelHeaderColorDark lets a project use a different color in dark mode.
62
+ $skinPanelHeaderColor: var(--aa-page-bg)!default;
63
+ $skinPanelHeaderColorDark: var(--aa-page-bg)!default;
64
+ // Panel/section header TEXT color (the label on the colored header bar), per mode.
65
+ $skinPanelHeaderTextColor: var(--aa-inactive-tab-text)!default;
66
+ $skinPanelHeaderTextColorDark: var(--aa-inactive-tab-text)!default;
67
+
68
+ // Form label / legend text — a muted blue-grey in light; the regular text color
69
+ // in dark (where the muted blue-grey would be too dim).
70
+ $skinLabelColor: #8494a8!default;
71
+ $skinLabelColorDark: $skinTextColorDark!default;
72
+
73
+ // Buttons (form submit, title-bar action buttons, batch actions). Separate from
74
+ // the accent so buttons can be a non-blue color, with a dark-mode override.
75
+ // Darker than the accent because the label is white: the accent itself gives
76
+ // 2.74:1, under the 4.5:1 small text needs. At -20% it is 5.35:1, and it is the
77
+ // tone dark mode already used, so the button is now one colour in both modes.
78
+ $skinButtonColor: darken($skinMainSecondColor, 20%)!default;
79
+ // Label on those buttons. A project is invited above to pick a non-blue fill;
80
+ // without this the label stays white and a light fill is unreadable.
81
+ $skinButtonTextColor: #ffffff!default;
82
+ $skinButtonTextColorDark: $skinButtonTextColor!default;
83
+ $skinButtonColorDark: $skinButtonColor!default;
84
+
85
+ // Vertical padding controlling the height of panel/section headers and tabs.
86
+ $skinPanelHeaderPaddingY: 5px!default; // panel + sidebar header height
87
+ $skinTabPaddingY: 8px!default; // tab height
88
+ $skinTabPaddingX: 15px!default; // tab label horizontal padding (text → border)
89
+ // Height of every table-tools button (scopes, batch actions, view switcher,
90
+ // custom tools) — matched to the panel-header height.
91
+ $skinTableToolsHeight: 30px!default;
92
+ // Horizontal text padding inside each table-tools button (controls button width).
93
+ // Derived from the button height so the two scale together.
94
+ $skinTableToolsPaddingX: $skinTableToolsHeight * 0.4 !default; // 12px at 30px
95
+ // Per-group table-tools button fills. All default to the surface palette so the
96
+ // whole row shares one neutral look; override any group independently (both modes).
97
+
98
+ // Index-table column header text (both the sortable links and plain headers use
99
+ // one color, configurable per mode).
100
+
101
+ // Inactive tab background. Defaults to the panel-header color so tabs stay
102
+ // consistent, but can be set separately (e.g. a more subdued tab bar).
103
+
104
+ // Tab text colors — the active (selected) tab and the inactive tabs, per mode.
105
+
106
+ // Content link color (generic links inside the admin content), per mode.
107
+ // Focus ring / accent outline. Follows the theme accent, so a project that
108
+ // rebrands $skinMainSecondColor does not keep a stray blue focus border.
109
+ $skinAccentColor: $skinMainSecondColor!default;
110
+ $skinAccentColorDark: $skinAccentColor!default;
111
+ // Darker than the theme accent on purpose: the accent is a fill colour and
112
+ // fails WCAG AA as body text — #5ea3d3 is 2.74 on white, where 4.5 is the bar
113
+ // for text this size — and links also sit on zebra rows, table headers and
114
+ // selected rows, so the worst of those is what has to clear it. #1f5f8d
115
+ // keeps the hue and gives 5.28 against the darkest of them.
116
+ $skinLinkColor: #38678b!default;
117
+ $skinLinkColorDark: #7cc0ec!default;
118
+
119
+ // Delete / destroy links. Light follows the regular link color (so action links
120
+ // match); dark uses a lightened red, since the default red is too dark there.
121
+ $skinDeleteLinkColor: $skinLinkColor!default;
122
+ $skinDeleteLinkColorDark: #f49b9b!default;
123
+ // Tabs: fill and label colours per mode. yeti-web sets all four to make the
124
+ // inactive tabs recede to the page background and the active one read as raised.
125
+ $skinTabInactiveColor: #f7f9fb!default;
126
+ $skinTabInactiveColorDark: #161a1e!default;
127
+ $skinActiveTabTextColor: $skinMainSecondColor!default;
128
+ $skinActiveTabTextColorDark: #7cc0ec!default;
129
+ $skinInactiveTabTextColor: #5e6469!default;
130
+ $skinInactiveTabTextColorDark: #b0b8c2!default;
131
+ // Index-table column header text, one colour for sortable and plain headers.
132
+ $skinTableHeaderTextColor: $skinTextColor!default;
133
+ $skinTableHeaderTextColorDark: #dde2e8!default;
134
+
135
+ // Status tags. The label is the same on every filled tag in both modes. The
136
+ // fills below are dark enough to carry a white one: every one of the five is
137
+ // at least 4.53 against it. The mid-tone fills this palette replaces ran from
138
+ // 2.35 to 3.78, under the 4.5 WCAG AA asks of text this small, which is why
139
+ // they moved rather than the label.
140
+ $skinStatusTagTextColor: #ffffff!default;
141
+ $skinStatusTagNeutralColor: #707681!default; // "No", protocol tags, anything unclassified
142
+ $skinStatusTagOkColor: #5e7e63!default; // ok / published / complete / green / yes
143
+ $skinStatusTagNoticeColor: #3874d2!default; // notice / blue
144
+ $skinStatusTagWarnColor: #9e6c15!default; // warn / warning / orange
145
+ $skinStatusTagErrorColor: #ce483b!default; // error / errored / red
146
+
147
+ //DARK-MODE PALETTE---------------------------------------------------------------------------------------------------//
148
+ // Semantic CSS custom properties for runtime light/dark switching. Light values
149
+ // (= the existing look) live on :root; the dark values are applied either by the
150
+ // OS preference (unless the user forced light) or by an explicit html[data-theme].
151
+ // Phase 0: defined only — surfaces are repointed to these vars in later phases,
152
+ // so there is no visual change yet.
153
+ @mixin aa-dark-palette {
154
+ // Render native controls (checkboxes, radios, native selects, scrollbars) dark.
155
+ color-scheme: dark;
156
+ --aa-page-bg: #{$skinPageBgColorDark};
157
+ --aa-surface: #{$skinSurfaceColorDark};
158
+ --aa-surface-2: #{$skinSurface2ColorDark};
159
+ // Not $skinSurface2ColorDark: that is the zebra fill, and feeding both from
160
+ // one variable left the header band and every other row byte-identical.
161
+ // Light mode never had that problem — $skinBorderWindowColor gives the
162
+ // header its own step there.
163
+ --aa-table-header: #{$skinElevatedColorDark};
164
+ --aa-surface-hover: #{$skinSurfaceHoverColorDark};
165
+ --aa-selected: #{$skinSelectedRowColorDark};
166
+ --aa-elevated: #{$skinElevatedColorDark};
167
+ --aa-text: #{$skinTextColorDark};
168
+ --aa-text-muted: #{$skinTextMutedColorDark};
169
+ --aa-border: #{$skinBorderColorDark};
170
+ --aa-link: #{$skinLinkColorDark};
171
+ --aa-accent: #{$skinAccentColorDark};
172
+ --aa-input-bg: #{$skinInputBgColorDark};
173
+ --aa-input-border: #{$skinInputBorderColorDark};
174
+ --aa-shadow: rgba(0, 0, 0, 0.45);
175
+ --aa-panel-header: #{$skinPanelHeaderColorDark};
176
+ --aa-panel-header-text: #{$skinPanelHeaderTextColorDark};
177
+ --aa-label-text: #{$skinLabelColorDark};
178
+ --aa-button: #{$skinButtonColorDark};
179
+ --aa-button-text: #{$skinButtonTextColorDark};
180
+ --aa-button-hover: #{darken($skinButtonColorDark, 8%)};
181
+ --aa-table-header-text: #{$skinTableHeaderTextColorDark};
182
+ --aa-tab-inactive: #{$skinTabInactiveColorDark};
183
+ --aa-active-tab-text: #{$skinActiveTabTextColorDark};
184
+ --aa-inactive-tab-text: #{$skinInactiveTabTextColorDark};
185
+ --aa-delete-link: #{$skinDeleteLinkColorDark};
186
+ }
187
+ :root {
188
+ color-scheme: light;
189
+ --aa-page-bg: #{$skinPageBgColor};
190
+ --aa-surface: #{$skinSurfaceColor};
191
+ --aa-surface-2: #{$skinSurface2Color};
192
+ // Index-table headers keep following $skinBorderWindowColor, the knob the
193
+ // README has documented for years, instead of the new surface scale.
194
+ --aa-table-header: #{$skinBorderWindowColor};
195
+ --aa-surface-hover: #{$skinSurfaceHoverColor};
196
+ --aa-selected: #{$skinSelectedRowColor};
197
+ --aa-elevated: #{$skinElevatedColor};
198
+ --aa-text: #{$skinTextColor};
199
+ --aa-text-muted: #{$skinTextMutedColor};
200
+ --aa-border: #{$skinBorderColor};
201
+ --aa-link: #{$skinLinkColor};
202
+ --aa-accent: #{$skinAccentColor};
203
+ --aa-input-bg: #{$skinInputBgColor};
204
+ --aa-input-border: #{$skinInputBorderColor};
205
+ --aa-shadow: rgba(0, 0, 0, 0.08);
206
+ --aa-panel-header: #{$skinPanelHeaderColor};
207
+ --aa-panel-header-text: #{$skinPanelHeaderTextColor};
208
+ --aa-label-text: #{$skinLabelColor};
209
+ --aa-button: #{$skinButtonColor};
210
+ --aa-button-text: #{$skinButtonTextColor};
211
+ --aa-button-hover: #{darken($skinButtonColor, 8%)};
212
+ --aa-table-header-text: #{$skinTableHeaderTextColor};
213
+ --aa-tab-inactive: #{$skinTabInactiveColor};
214
+ --aa-active-tab-text: #{$skinActiveTabTextColor};
215
+ --aa-inactive-tab-text: #{$skinInactiveTabTextColor};
216
+ --aa-delete-link: #{$skinDeleteLinkColor};
217
+ }
218
+ // OS preference, unless the user explicitly chose light
219
+ @media (prefers-color-scheme: dark) {
220
+ html:not([data-theme="light"]) { @include aa-dark-palette; }
221
+ }
222
+ // Explicit user choice always wins
223
+ html[data-theme="dark"] { @include aa-dark-palette; }
224
+
225
+ // Public helper so a host app can scope its own (project-specific) overrides to
226
+ // dark mode using the same triggers. Use at the top level and put full selectors
227
+ // inside; the --aa-* custom properties are available:
228
+ // @include aa-dark { .my-widget { background: var(--aa-surface); } }
229
+ @mixin aa-dark {
230
+ @media (prefers-color-scheme: dark) {
231
+ html:not([data-theme="light"]) { @content; }
232
+ }
233
+ html[data-theme="dark"] { @content; }
234
+ }
235
+
236
+ // Иконки переключателя тем. Инлайном, потому что гем не возит картинок, и
237
+ // маской, а не цветным SVG: маска красится currentColor и сама идёт за
238
+ // $skinMenuTextColor. Половинка круга — auto, солнце — light, месяц — dark.
239
+ $theme-icon-auto: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.6' fill='none' stroke='black' stroke-width='1.4'/%3E%3Cpath d='M8 2.6a5.4 5.4 0 0 1 0 10.8z'/%3E%3C/svg%3E")!default;
240
+ $theme-icon-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round'%3E%3Ccircle cx='8' cy='8' r='3.1'/%3E%3Cpath d='M8 .9v1.8M8 13.3v1.8M.9 8h1.8M13.3 8h1.8M2.9 2.9l1.3 1.3M11.8 11.8l1.3 1.3M13.1 2.9l-1.3 1.3M4.2 11.8l-1.3 1.3'/%3E%3C/svg%3E")!default;
241
+ $theme-icon-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M13.4 9.6A5.9 5.9 0 0 1 6.4 2.6a5.9 5.9 0 1 0 7 7z'/%3E%3C/svg%3E")!default;
242
+
243
+ // A wrong-typed override is legal SassScript, so it compiles without a word and
244
+ // emits CSS the browser drops — `border-bottom: none solid #5ea3d3`,
245
+ // `padding: 10 12px`. The rule silently vanishes and ActiveAdmin's own value
246
+ // reappears. Fail the build with a readable message instead.
247
+ @each $name, $value in (
248
+ skinBorderRadius: $skinBorderRadius,
249
+ skinTablePadding: $skinTablePadding,
250
+ skinMenuFontSize: $skinMenuFontSize,
251
+ skinMenuItemPaddingY: $skinMenuItemPaddingY,
252
+ skinMenuPanelMaxWidth: $skinMenuPanelMaxWidth,
253
+ skinHeaderPaddingTop: $skinHeaderPaddingTop,
254
+ skinHeaderPaddingBottom: $skinHeaderPaddingBottom,
255
+ skinTitleBarBorderWidth: $skinTitleBarBorderWidth,
256
+ skinTitleBarButtonPaddingY: $skinTitleBarButtonPaddingY,
257
+ skinTitleBarButtonPaddingX: $skinTitleBarButtonPaddingX
258
+ ) {
259
+ @if type-of($value) != number or (unitless($value) and $value != 0) {
260
+ @error "$#{$name} must be a length with a unit (e.g. 10px), got `#{$value}`.";
261
+ }
262
+ }
263
+ @each $name, $value in (
264
+ skinMainFirstColor: $skinMainFirstColor,
265
+ skinMainSecondColor: $skinMainSecondColor,
266
+ skinBorderWindowColor: $skinBorderWindowColor,
267
+ skinMenuPillColor: $skinMenuPillColor,
268
+ skinMenuPillTextColor: $skinMenuPillTextColor,
269
+ skinMenuPanelColor: $skinMenuPanelColor,
270
+ skinMenuItemHoverColor: $skinMenuItemHoverColor,
271
+ skinMenuTextColor: $skinMenuTextColor,
272
+ skinTitleBarColor: $skinTitleBarColor,
273
+ skinTitleBarBorderColor: $skinTitleBarBorderColor,
274
+ skinSelectedRowColor: $skinSelectedRowColor,
275
+ skinSelectedRowColorDark: $skinSelectedRowColorDark,
276
+ skinAccentColor: $skinAccentColor,
277
+ skinAccentColorDark: $skinAccentColorDark,
278
+ skinButtonTextColor: $skinButtonTextColor,
279
+ skinButtonTextColorDark: $skinButtonTextColorDark,
280
+ skinPageBgColor: $skinPageBgColor,
281
+ skinPageBgColorDark: $skinPageBgColorDark,
282
+ skinSurfaceColor: $skinSurfaceColor,
283
+ skinSurfaceColorDark: $skinSurfaceColorDark,
284
+ skinSurface2Color: $skinSurface2Color,
285
+ skinSurface2ColorDark: $skinSurface2ColorDark,
286
+ skinSurfaceHoverColor: $skinSurfaceHoverColor,
287
+ skinSurfaceHoverColorDark: $skinSurfaceHoverColorDark,
288
+ skinElevatedColor: $skinElevatedColor,
289
+ skinElevatedColorDark: $skinElevatedColorDark,
290
+ skinTextColor: $skinTextColor,
291
+ skinTextColorDark: $skinTextColorDark,
292
+ skinTextMutedColor: $skinTextMutedColor,
293
+ skinTextMutedColorDark: $skinTextMutedColorDark,
294
+ skinBorderColor: $skinBorderColor,
295
+ skinBorderColorDark: $skinBorderColorDark,
296
+ skinInputBgColor: $skinInputBgColor,
297
+ skinInputBgColorDark: $skinInputBgColorDark,
298
+ skinInputBorderColor: $skinInputBorderColor,
299
+ skinInputBorderColorDark: $skinInputBorderColorDark,
300
+ skinMenuItemHoverTextColor: $skinMenuItemHoverTextColor,
301
+ skinLabelColor: $skinLabelColor,
302
+ skinLabelColorDark: $skinLabelColorDark,
303
+ skinButtonColor: $skinButtonColor,
304
+ skinButtonColorDark: $skinButtonColorDark,
305
+ skinLinkColor: $skinLinkColor,
306
+ skinLinkColorDark: $skinLinkColorDark,
307
+ skinDeleteLinkColor: $skinDeleteLinkColor,
308
+ skinDeleteLinkColorDark: $skinDeleteLinkColorDark,
309
+ skinTabInactiveColor: $skinTabInactiveColor,
310
+ skinTabInactiveColorDark: $skinTabInactiveColorDark,
311
+ skinActiveTabTextColor: $skinActiveTabTextColor,
312
+ skinActiveTabTextColorDark: $skinActiveTabTextColorDark,
313
+ skinInactiveTabTextColor: $skinInactiveTabTextColor,
314
+ skinInactiveTabTextColorDark: $skinInactiveTabTextColorDark,
315
+ skinTableHeaderTextColor: $skinTableHeaderTextColor,
316
+ skinTableHeaderTextColorDark: $skinTableHeaderTextColorDark,
317
+ skinStatusTagTextColor: $skinStatusTagTextColor,
318
+ skinStatusTagNeutralColor: $skinStatusTagNeutralColor,
319
+ skinStatusTagOkColor: $skinStatusTagOkColor,
320
+ skinStatusTagNoticeColor: $skinStatusTagNoticeColor,
321
+ skinStatusTagWarnColor: $skinStatusTagWarnColor,
322
+ skinStatusTagErrorColor: $skinStatusTagErrorColor
323
+ ) {
324
+ @if type-of($value) != color {
325
+ @error "$#{$name} must be a color (use `transparent`, not `none`), got `#{$value}`.";
326
+ }
327
+ }
328
+ // The panel header pair defaults to a custom property and is documented as
329
+ // accepting one, so a plain `type-of != color` would reject the shipped value.
330
+ @each $name, $value in (
331
+ skinPanelHeaderColor: $skinPanelHeaderColor,
332
+ skinPanelHeaderColorDark: $skinPanelHeaderColorDark,
333
+ skinPanelHeaderTextColor: $skinPanelHeaderTextColor,
334
+ skinPanelHeaderTextColorDark: $skinPanelHeaderTextColorDark
335
+ ) {
336
+ @if type-of($value) != color and str-index("#{$value}", "var(") != 1 {
337
+ @error "$#{$name} must be a color or a CSS custom property (e.g. var(--aa-page-bg)), got `#{$value}`.";
338
+ }
339
+ }
340
+ // Радиус внутреннего угла ячейки — на 1px меньше внешнего, чтобы не торчал
341
+ // из-под рамки таблицы. Вычитать px напрямую нельзя: $skinBorderRadius задаёт
342
+ // проект, и 0.25rem роняет всю компиляцию с «Incompatible units». calc()
343
+ // откладывает вычитание до браузера, а безразмерный 0 обрабатываем отдельно —
344
+ // calc(0 - 1px) невалиден.
345
+ @function inner-radius($radius) {
346
+ @if unitless($radius) { @return 0; }
347
+ @return calc(#{$radius} - 1px);
348
+ }
349
+
8
350
  //HTML----------------------------------------------------------------------------------------------------------------//
9
351
 
10
352
  html {
11
- background: #f7f9fb;
353
+ background: var(--aa-page-bg);
12
354
  }
13
355
  body.active_admin {
14
- background: #f7f9fb;
356
+ background: var(--aa-page-bg);
15
357
  }
16
358
  #wrapper {
17
359
  div.tabs {
@@ -21,11 +363,15 @@ body.active_admin {
21
363
  text-shadow: none;
22
364
  box-shadow: none;
23
365
  margin-bottom: 11px;
24
- border: none;
25
- background-color: $skinMainSecondColor;
26
- color: #ffffff;
366
+ // Top/side outline only; no bottom outline (the content panel's own top
367
+ // border is the baseline, drawn over the tabs via z-index below).
368
+ border: 1px solid var(--aa-border);
369
+ border-bottom: 0;
370
+ vertical-align: top;
371
+ background-color: var(--aa-tab-inactive);
372
+ color: var(--aa-inactive-tab-text);
27
373
  margin-right: 5px;
28
- padding: 10px 20px;
374
+ padding: $skinTabPaddingY $skinTabPaddingX;
29
375
 
30
376
  -webkit-border-top-left-radius: $skinBorderRadius;
31
377
  -webkit-border-top-right-radius: $skinBorderRadius;
@@ -42,68 +388,164 @@ body.active_admin {
42
388
  -moz-border-radius-bottomright: 0;
43
389
  border-bottom-right-radius: 0;
44
390
  }
391
+ // Active tab sits above the content panel (z-index: 2) and is filled with the
392
+ // content color, so it covers the panel's top border there and merges in.
45
393
  ul > li.ui-state-active > a {
46
- background-color: #ffffff;
47
- border: 1px solid $skinBorderWindowColor;
48
- border-bottom: 0;
49
- color: $skinMainSecondColor;
394
+ background-color: var(--aa-surface);
395
+ border: 1px solid var(--aa-border);
396
+ border-bottom-color: var(--aa-surface);
397
+ color: var(--aa-active-tab-text);
398
+ position: relative;
399
+ z-index: 2;
400
+ vertical-align: top;
50
401
  }
51
402
  div.tab-content {
52
- background-color: #ffffff;
53
- border: 1px solid $skinBorderWindowColor;
54
- border-bottom: 2px solid $skinBorderWindowColor;
403
+ background-color: var(--aa-surface);
404
+ // Uniform 1px border (no thicker bottom) so the rounded corners are even.
405
+ border: 1px solid var(--aa-border);
55
406
  @include rounded($skinBorderRadius);
407
+ // Paint above the inactive tabs so the panel's own top border is the
408
+ // baseline drawn over them (the active tab paints above this, z-index: 2).
409
+ position: relative;
410
+ z-index: 1;
56
411
  }
57
412
  }
58
413
  #header {
59
414
  background-color: $skinMainFirstColor;
60
415
  border-bottom: 1px solid lighten( $skinMainFirstColor, 15% )!important;
61
- padding-bottom: 9px;
416
+ padding-top: $skinHeaderPaddingTop;
417
+ padding-bottom: $skinHeaderPaddingBottom;
62
418
  border: none;
63
419
  text-shadow: none;
64
420
  background-image: none;
65
421
  h1.site_title {
66
422
  color: $skinMainSecondColor;
67
423
  font-size: 16px;
424
+ img {
425
+ max-height: $skinHeaderLogoMaxHeight;
426
+ }
68
427
  }
69
428
  a, a:link {
70
429
  color: #ffffff;
71
430
  }
72
431
  ul.tabs > li > a {
73
- color: #ffffff;
432
+ // On the anchor, not the li, so the dropdown subtree does not inherit it.
433
+ font-size: $skinMenuFontSize;
74
434
  @include rounded($skinBorderRadius);
75
435
  padding: 6px 10px 6px 10px;
76
436
  }
77
437
  ul.tabs > li.current > a, ul.tabs > li:hover > a {
78
- background-color: $skinMainSecondColor;
438
+ background-color: $skinMenuPillColor;
439
+ color: $skinMenuPillTextColor;
440
+ }
441
+ // ActiveAdmin swaps in its "dark" submenu arrow on the current/hovered item,
442
+ // which assumes the highlight is lighter than the bar. The default pill is
443
+ // not, so keep the light arrow there.
444
+ ul.tabs > li.has_nested.current > a, ul.tabs > li.has_nested:hover > a {
445
+ background-image: url($menu-arrow-light-icon-url);
446
+ }
447
+ // The utility nav (username, clock, theme switcher, logout) are not navigation
448
+ // tabs: drop the pill padding, and on hover highlight by text colour only — no
449
+ // background block. The clock has no click action, so it gets no hover at all.
450
+ #utility_nav {
451
+ > li > a { padding: 0 5px; }
452
+ > li:hover > a, > li.current > a { background-color: transparent; }
453
+ > li#servertime:hover > a { color: #ffffff; }
454
+
455
+ // The theme switch (app/assets/javascripts/wigu/theme_toggle.js). The
456
+ // script only writes data-mode; the glyph is drawn here, so a project can
457
+ // swap it for its own icon font without forking the script. Masked rather
458
+ // than embedded as a coloured SVG so it takes currentColor and follows
459
+ // $skinMenuTextColor like every other item in the bar.
460
+ // On the li, not only the anchor: font-size 0 collapses the anchor to the
461
+ // icon's own 16px, which then sits at the top of the item instead of on
462
+ // the line the rest of the bar shares. inline-flex, not flex: ActiveAdmin
463
+ // lays the utility nav out as `li { display: inline }`, and a block-level
464
+ // item breaks the row, dropping the items after it onto new lines.
465
+ > li#theme_toggle { display: inline-flex; align-items: center; vertical-align: middle; }
466
+ > li#theme_toggle > a {
467
+ display: flex;
468
+ align-items: center;
469
+ font-size: 0;
470
+ opacity: 0.75;
471
+
472
+ &:before {
473
+ content: "";
474
+ display: block;
475
+ width: 16px;
476
+ height: 16px;
477
+ background-color: currentColor;
478
+ -webkit-mask: #{$theme-icon-auto} center / contain no-repeat;
479
+ mask: #{$theme-icon-auto} center / contain no-repeat;
480
+ }
481
+ &:hover, &:focus { opacity: 1; }
482
+ }
483
+ > li#theme_toggle[data-mode="light"] > a:before {
484
+ -webkit-mask-image: $theme-icon-light;
485
+ mask-image: $theme-icon-light;
486
+ }
487
+ > li#theme_toggle[data-mode="dark"] > a:before {
488
+ -webkit-mask-image: $theme-icon-dark;
489
+ mask-image: $theme-icon-dark;
490
+ }
79
491
  }
80
492
  ul.tabs {
81
493
  li.has_nested {
82
- a {
494
+ // `> a` only: leaf items need no room for the submenu arrow.
495
+ > a {
83
496
  padding-right: 20px;
84
497
  }
85
498
  ul {
86
- background-color: $skinMainSecondColor;
87
- @include rounded($skinBorderRadius);
499
+ background-color: $skinMenuPanelColor;
500
+ @include rounded-all(0, $skinBorderRadius, $skinBorderRadius, $skinBorderRadius);
501
+ // Size to content instead of the base's max-width: calc(100% + 20px),
502
+ // which wraps long labels onto a second line — but keep a ceiling, or
503
+ // a long label pushes the panel out of the viewport where the :hover
504
+ // chain cannot follow it. Past the ceiling, labels wrap as before.
505
+ // Known limit: a depth-2 flyout under a right-edge tab can still leave
506
+ // a 1280px viewport — it sits at left:100% of an already-offset panel,
507
+ // and CSS cannot see the distance to the viewport edge. ActiveAdmin's
508
+ // own 175px panels already land within 5px of the edge there, so any
509
+ // widening inherits this. A full fix needs JS that flips the flyout to
510
+ // `right:100%`, or CSS anchor positioning once it is baseline.
511
+ width: max-content;
512
+ max-width: $skinMenuPanelMaxWidth;
513
+ overflow-wrap: break-word;
88
514
  li > a {
89
515
  padding-left: 15px;
516
+ color: $skinMenuTextColor;
517
+ line-height: $skinMenuItemLineHeight;
518
+ padding-top: $skinMenuItemPaddingY;
519
+ padding-bottom: $skinMenuItemPaddingY;
520
+ }
521
+ li:hover > a, li.current > a, li > a:focus {
522
+ background-color: $skinMenuItemHoverColor;
523
+ color: $skinMenuItemHoverTextColor;
90
524
  }
91
- li.current > a::before, li:hover > a::before {
525
+ li.current > a::before, li:hover > a::before, li > a:focus::before {
92
526
  content: '';
93
527
  display: block;
94
- width: 7px;
95
- height: 4px;
96
528
  position: absolute;
97
- top: 12px;
529
+ top: 50%;
98
530
  left: 4px;
99
- background: transparent url($menu-arrow-dark-icon-url) no-repeat 0 0;
100
- transform: rotate(-90deg);
531
+ // currentColor, so the marker follows $skinMenuTextColor instead of
532
+ // a fixed-palette PNG that only reads against the default panel.
533
+ border: 3px solid transparent;
534
+ border-left-color: currentColor;
535
+ margin-top: -3px;
101
536
  }
102
537
  }
103
538
  }
104
- li.has_nested:hover {
105
- a {
106
- border-bottom: 7px solid $skinMainSecondColor;
539
+ // `> li.has_nested` + `> a` so the "bridge" border is added only to a
540
+ // hovered TOP-LEVEL menu item (bridging its pill to the dropdown). Without
541
+ // the `>`, nested has_nested items (e.g. System > Components) also got the
542
+ // border, which pushed the next dropdown item down when their submenu
543
+ // opened. 5px to match the base's `ul { margin-top: 5px }` gap, and square
544
+ // off the corners that face the panel so the header does not show through.
545
+ > li.has_nested:hover {
546
+ > a {
547
+ @include rounded-top($skinBorderRadius);
548
+ border-bottom: 5px solid $skinMenuPanelColor;
107
549
  }
108
550
  }
109
551
  }
@@ -112,9 +554,9 @@ body.active_admin {
112
554
  background-image: none;
113
555
  border: none;
114
556
  box-shadow: none;
115
- background-color: lighten( $skinMainFirstColor, 8% );
557
+ background-color: $skinTitleBarColor;
116
558
  border: none;
117
- border-bottom: 3px solid $skinMainSecondColor;
559
+ border-bottom: $skinTitleBarBorderWidth solid $skinTitleBarBorderColor;
118
560
  #titlebar_left {
119
561
  span.breadcrumb {
120
562
  font-size: 12px;
@@ -130,62 +572,61 @@ body.active_admin {
130
572
  }
131
573
  }
132
574
  #titlebar_right {
133
- .action_item a {
575
+ // `> a` and `> .dropdown_menu > a`: an action item may hold a dropdown, and
576
+ // a descendant selector here paints its menu entries with the button fill
577
+ // too — with !important, so the entry rules below cannot undo it.
578
+ .action_item > a,
579
+ .action_item > .dropdown_menu > a {
134
580
  background-image: none;
135
581
  border: none;
136
582
  box-shadow: none;
137
583
  text-shadow: none;
138
584
  @include rounded($skinBorderRadius);
139
- background-color: $skinMainSecondColor;
585
+ background-color: var(--aa-button);
140
586
  border: none;
141
- padding: 10px 20px;
142
- color: #ffffff;
587
+ padding: $skinTitleBarButtonPaddingY $skinTitleBarButtonPaddingX;
588
+ color: var(--aa-button-text);
143
589
  font-weight: 600;
144
590
  &:hover {
145
- background-color: lighten( $skinMainSecondColor, 5% )!important;
591
+ background-color: var(--aa-button-hover)!important;
146
592
  }
147
593
  }
148
594
  div.dropdown_menu {
149
595
  div.dropdown_menu_list_wrapper {
150
- background-color: $skinMainFirstColor;
151
- border: none;
596
+ background-color: var(--aa-elevated);
597
+ border: 1px solid var(--aa-border);
152
598
  background-image: none;
153
- border: none;
154
599
  box-shadow: none;
155
- div.dropdown_menu_nipple, div.dropdown_menu_nipple:after, div.dropdown_menu_nipple:before {
156
- border-color: $skinMainFirstColor transparent;
600
+ // ActiveAdmin draws the nipple as three stacked triangles: the element
601
+ // itself is the outline, :before the inner shadow, :after the fill.
602
+ // Painting all three the same colour collapses it into a solid arrow
603
+ // that is invisible whenever the panel fill is close to the page —
604
+ // which is exactly what a light surface is. Outline it like the panel.
605
+ div.dropdown_menu_nipple {
606
+ border-color: var(--aa-border) transparent;
607
+ &:before, &:after { border-color: var(--aa-elevated) transparent; }
157
608
  }
158
609
  ul.dropdown_menu_list {
159
610
  box-shadow: none;
160
611
  li {
161
612
  a {
162
- background-color: #ffffff;
163
- color: $skinMainFirstColor;
613
+ background-color: var(--aa-surface);
614
+ color: var(--aa-text);
164
615
  border-radius: 0;
165
616
  &:hover {
166
- background-color: $skinMainSecondColor;
167
- color: #ffffff;
617
+ // background-image, not just background-color: ActiveAdmin
618
+ // paints this hover with a linear-gradient, and a gradient
619
+ // sits on top of the background colour — leaving it in place
620
+ // made every colour set here invisible.
621
+ background-image: none;
622
+ background-color: var(--aa-selected);
623
+ color: var(--aa-text);
168
624
  }
169
625
  }
170
626
  }
171
627
  }
172
628
  }
173
629
  }
174
- div.batch_actions_selector {
175
- a {
176
- background-image: none;
177
- border: none;
178
- box-shadow: none;
179
- background-color: $skinMainSecondColor;
180
- @include rounded($skinBorderRadius);
181
- border: none;
182
- padding: 10px 20px;
183
- color: #ffffff;
184
- &:hover {
185
- background-color: lighten( $skinMainSecondColor, 5% );
186
- }
187
- }
188
- }
189
630
  }
190
631
  }
191
632
  .flashes {
@@ -210,32 +651,77 @@ body.active_admin {
210
651
  }
211
652
  #active_admin_content {
212
653
  span.blank_slate {
213
- border: 2px solid lighten( $skinMainSecondColor, 30% );
214
- color: #8494a8;
654
+ // Was a hardcoded lightened blue (too bright in dark mode); use the palette
655
+ // border + muted text so it adapts to light/dark.
656
+ border: 2px solid var(--aa-border);
657
+ color: var(--aa-text-muted);
215
658
  @include rounded($skinBorderRadius);
216
659
  }
217
660
  div.table_tools {
218
- margin-bottom: 35px;
661
+ margin-bottom: 14px; // match the gap between sidebar sections
662
+ // The gem shrinks the scope / batch / tool buttons to 0.9em; reset to 1em so
663
+ // they match the surrounding base text — the same size the panel headers use.
664
+ .scopes a, a.table_tools_button, .dropdown_menu_button { font-size: 1em; }
665
+ // Contain the floated buttons so the margin sits below them. The collapsible-
666
+ // sidebar gem sets `.table_tools:after { clear: none; padding-bottom: 16px }`,
667
+ // which disables the float clearfix and otherwise leaves the box ~16px tall
668
+ // with the 29px buttons overflowing into the margin.
669
+ display: flow-root;
670
+ &::after { padding-bottom: 0; }
671
+ // Gap between the batch-actions button and the scopes — only rendered when
672
+ // the button is present, so scope-only pages stay flush with the table edge.
673
+ div.batch_actions_selector { margin-right: 10px; }
674
+ // Baseline for any table_tools button (the index-list view switcher, custom
675
+ // tools like "Visible columns", etc.). The base AA style is a light gradient;
676
+ // theme it via the --aa-* vars. Scopes override the fill + segmented-control
677
+ // geometry below (they reset border-radius and re-round only the ends).
678
+ // .dropdown_menu_button is the same control with a different class (the
679
+ // "Visible columns" style tools); without it here it keeps ActiveAdmin's
680
+ // white gradient pill at 22px next to the themed 30px buttons.
681
+ a.table_tools_button, a.dropdown_menu_button {
682
+ box-sizing: border-box;
683
+ height: $skinTableToolsHeight;
684
+ line-height: $skinTableToolsHeight; // = button height: nudges the text to the optical centre (the font otherwise sits high in the line box)
685
+ background: var(--aa-elevated);
686
+ background-image: none;
687
+ border: 1px solid var(--aa-border);
688
+ color: var(--aa-text);
689
+ @include rounded($skinBorderRadius);
690
+ padding: 0 $skinTableToolsPaddingX;
691
+ box-shadow: none;
692
+ text-shadow: none;
693
+ &:hover { background: var(--aa-surface-hover); }
694
+ }
219
695
  div.dropdown_menu {
220
696
  div.dropdown_menu_list_wrapper {
221
- background-color: $skinMainFirstColor;
222
- border: none;
697
+ background-color: var(--aa-elevated);
698
+ border: 1px solid var(--aa-border);
223
699
  background-image: none;
224
- border: none;
225
700
  box-shadow: none;
226
- div.dropdown_menu_nipple, div.dropdown_menu_nipple:after, div.dropdown_menu_nipple:before {
227
- border-color: $skinMainFirstColor transparent;
701
+ // ActiveAdmin draws the nipple as three stacked triangles: the element
702
+ // itself is the outline, :before the inner shadow, :after the fill.
703
+ // Painting all three the same colour collapses it into a solid arrow
704
+ // that is invisible whenever the panel fill is close to the page —
705
+ // which is exactly what a light surface is. Outline it like the panel.
706
+ div.dropdown_menu_nipple {
707
+ border-color: var(--aa-border) transparent;
708
+ &:before, &:after { border-color: var(--aa-elevated) transparent; }
228
709
  }
229
710
  ul.dropdown_menu_list {
230
711
  box-shadow: none;
231
712
  li {
232
713
  a {
233
- background-color: #ffffff;
234
- color: $skinMainFirstColor;
714
+ background-color: var(--aa-surface);
715
+ color: var(--aa-text);
235
716
  border-radius: 0;
236
717
  &:hover {
237
- background-color: $skinMainSecondColor;
238
- color: #ffffff;
718
+ // background-image, not just background-color: ActiveAdmin
719
+ // paints this hover with a linear-gradient, and a gradient
720
+ // sits on top of the background colour — leaving it in place
721
+ // made every colour set here invisible.
722
+ background-image: none;
723
+ background-color: var(--aa-selected);
724
+ color: var(--aa-text);
239
725
  }
240
726
  }
241
727
  }
@@ -243,40 +729,53 @@ body.active_admin {
243
729
  }
244
730
  }
245
731
  div.batch_actions_selector {
246
- a {
732
+ > a {
733
+ box-sizing: border-box;
734
+ height: $skinTableToolsHeight;
735
+ line-height: $skinTableToolsHeight;
247
736
  background-image: none;
248
- border: none;
249
737
  box-shadow: none;
250
- background-color: $skinMainSecondColor;
738
+ background-color: var(--aa-elevated);
251
739
  @include rounded($skinBorderRadius);
252
- border: none;
253
- padding: 10px 20px;
254
- color: #ffffff;
740
+ border: 1px solid var(--aa-border);
741
+ padding: 0 $skinTableToolsPaddingX;
742
+ color: var(--aa-text);
255
743
  &:hover {
256
- background-color: lighten( $skinMainSecondColor, 5% );
744
+ background-color: var(--aa-surface-hover);
257
745
  }
258
746
  }
259
747
  }
260
748
  div.dropdown_menu {
261
749
  div.dropdown_menu_list_wrapper {
262
- background-color: $skinMainFirstColor;
263
- border: none;
750
+ background-color: var(--aa-elevated);
751
+ border: 1px solid var(--aa-border);
264
752
  background-image: none;
265
- div.dropdown_menu_nipple, div.dropdown_menu_nipple:after, div.dropdown_menu_nipple:before {
266
- border-color: $skinMainFirstColor transparent;
753
+ // ActiveAdmin draws the nipple as three stacked triangles: the element
754
+ // itself is the outline, :before the inner shadow, :after the fill.
755
+ // Painting all three the same colour collapses it into a solid arrow
756
+ // that is invisible whenever the panel fill is close to the page —
757
+ // which is exactly what a light surface is. Outline it like the panel.
758
+ div.dropdown_menu_nipple {
759
+ border-color: var(--aa-border) transparent;
760
+ &:before, &:after { border-color: var(--aa-elevated) transparent; }
267
761
  }
268
762
  ul.dropdown_menu_list {
269
763
  box-shadow: none;
270
764
  li {
271
765
  a {
272
- background-color: #ffffff;
766
+ background-color: var(--aa-surface);
273
767
  box-shadow: none;
274
- color: $skinMainFirstColor;
768
+ color: var(--aa-text);
275
769
  border-radius: 0;
276
770
  text-shadow: none;
277
771
  &:hover {
278
- background-color: $skinMainSecondColor;
279
- color: #ffffff;
772
+ // background-image, not just background-color: ActiveAdmin
773
+ // paints this hover with a linear-gradient, and a gradient
774
+ // sits on top of the background colour — leaving it in place
775
+ // made every colour set here invisible.
776
+ background-image: none;
777
+ background-color: var(--aa-selected);
778
+ color: var(--aa-text);
280
779
  }
281
780
  }
282
781
  }
@@ -284,41 +783,61 @@ body.active_admin {
284
783
  }
285
784
  }
286
785
  div.batch_actions_selector {
287
- a {
786
+ > a {
787
+ box-sizing: border-box;
788
+ height: $skinTableToolsHeight;
789
+ line-height: $skinTableToolsHeight;
288
790
  background-image: none;
289
- background-color: $skinMainSecondColor;
791
+ background-color: var(--aa-elevated);
290
792
  @include rounded($skinBorderRadius);
291
- border: none;
292
- padding: 10px 20px;
293
- color: #ffffff;
793
+ border: 1px solid var(--aa-border);
794
+ padding: 0 $skinTableToolsPaddingX;
795
+ color: var(--aa-text);
294
796
  text-shadow: none;
295
797
  box-shadow: none;
296
798
  &:hover {
297
- background-color: lighten( $skinMainSecondColor, 5% );
799
+ background-color: var(--aa-surface-hover);
298
800
  box-shadow: none;
299
801
  }
300
802
  }
301
803
  }
302
- ul.scopes, div.scopes > ul {
804
+ // ActiveAdmin renders the index view switcher with the same classes as
805
+ // the scope bar — ul.indexes.table_tools_segmented_control, anchors
806
+ // carrying .table_tools_button — so it needs the same segmented
807
+ // geometry. Without it the switcher keeps the generic button radius on
808
+ // every segment, the shared borders double up, and the selected view is
809
+ // indistinguishable from the others.
810
+ ul.scopes, div.scopes > ul, ul.indexes {
303
811
  li {
304
812
  a {
305
813
  background-image: none;
306
- border: none;
814
+ // Reset the generic .table_tools_button radius — scopes are a segmented
815
+ // control, so only the first/last child are re-rounded (below).
816
+ @include rounded(0);
817
+ // Outline the segmented control with the border color (was a white
818
+ // right-divider, which is invisible when the scope bar matches the
819
+ // page background). Adjacent borders are collapsed below.
820
+ border: 1px solid var(--aa-border);
307
821
  box-shadow: none;
308
- background-color: $skinMainSecondColor;
309
- padding: 10px 20px;
310
- color: #ffffff;
822
+ background-color: var(--aa-surface);
823
+ padding: 0 $skinTableToolsPaddingX;
824
+ color: var(--aa-text);
311
825
  text-shadow: none;
312
- border-right: 1px solid #ffffff;
313
826
  span {
314
- color: #ffffff;
827
+ color: var(--aa-text);
315
828
  font-size: 11px;
316
829
  font-weight: bold;
317
830
  }
318
831
  }
319
832
  }
833
+ li + li a { margin-left: -1px; } // collapse the shared border between scopes
320
834
  li:first-child {
321
835
  a {
836
+ // Override AA's base `.scopes li:first-child a { margin-left: 10px }`.
837
+ // That gap belongs on the batch-actions button (margin-right below), so
838
+ // the scope bar aligns with the table's left edge when there are no
839
+ // batch actions.
840
+ margin-left: 0;
322
841
  -webkit-border-top-left-radius: $skinBorderRadius;
323
842
  -webkit-border-bottom-left-radius: $skinBorderRadius;
324
843
  -moz-border-radius-topleft: $skinBorderRadius;
@@ -339,16 +858,24 @@ body.active_admin {
339
858
  }
340
859
  li.selected {
341
860
  a {
342
- background-color: lighten( $skinMainSecondColor, 15% );
861
+ // Selected scope: a touch lighter than the neutral fill, plus an accent
862
+ // text colour so the active scope's label stands out.
863
+ background-color: var(--aa-surface-hover);
864
+ color: var(--aa-link);
865
+ span { color: var(--aa-link); }
343
866
  }
344
867
  }
345
868
  }
346
869
  }
347
870
  }
348
871
  table.index_table {
349
- background-color: #ffffff;
350
- border: 1px solid $skinBorderWindowColor;
351
- border-bottom: 2px solid $skinBorderWindowColor;
872
+ background-color: var(--aa-surface);
873
+ // Uniform 1px border (no thicker bottom) so the rounded corners are even.
874
+ border: 1px solid var(--aa-border);
875
+ // The right edge is owned by the last-column cells (below) rather than the
876
+ // table itself, so a last-column cell's own border-right doesn't stack
877
+ // against the table border 1px further out (a doubled right line).
878
+ border-right: 0;
352
879
  @include rounded($skinBorderRadius);
353
880
  th {
354
881
  background-image: none;
@@ -359,16 +886,93 @@ body.active_admin {
359
886
  font-weight: bold;
360
887
  background-color: $skinBorderWindowColor;
361
888
  padding: $skinTablePadding 12px $skinTablePadding 12px;
362
- border-bottom: 3px solid darken( $skinBorderWindowColor, 5% );
889
+ // 1px header underline (was 3px) so it matches the other 1px borders.
890
+ border-bottom: 1px solid darken( $skinBorderWindowColor, 5% );
363
891
  border-right: 1px solid darken( $skinBorderWindowColor, 5% );
364
892
  a {
365
893
  font-size: 1.0em;
366
894
  font-weight: bold!important;
367
895
  }
368
896
  }
897
+ // ActiveAdmin puts the sort arrow inside the heading link, on the left, as a
898
+ // background image cleared with `padding-left: 13px`. That indents the label
899
+ // of every sortable column by 13px while the data below starts at the cell's
900
+ // own padding, so a heading never lines up with its column. The link is also
901
+ // `display: block`, so moving the image to the right edge would park the
902
+ // arrow at the far side of the column instead of beside the text.
903
+ //
904
+ // Drawn as a pseudo-element instead: it follows the label immediately, the
905
+ // link keeps its full width so the whole cell stays clickable, and the
906
+ // marker takes currentColor — the stock sprite is a fixed grey PNG that
907
+ // cannot follow the text into dark mode.
908
+ // `a[href*="order="]`, not every anchor in the header: an application can
909
+ // put its own link in there — yeti-web adds a persistent-sort toggle — and
910
+ // it would otherwise get a sort marker of its own. ActiveAdmin's heading
911
+ // link always carries the order parameter.
912
+ th.sortable > a[href*="order="] {
913
+ padding-left: 0;
914
+ background-image: none;
915
+
916
+ &:after {
917
+ content: "";
918
+ display: inline-block;
919
+ margin-left: 6px;
920
+ vertical-align: middle;
921
+ // The unused side has no width rather than a transparent one, so the
922
+ // box is exactly as tall as the triangle in it. Keeping all four sides
923
+ // and nudging with a margin instead puts the two states at different
924
+ // heights, because `vertical-align: middle` centres the box and the
925
+ // visible half then sits off-centre within it.
926
+ border: 4px solid transparent;
927
+ border-bottom-width: 0;
928
+ border-top-color: currentColor;
929
+ // 0.6, not lower: the marker is the only thing separating a sortable
930
+ // heading from a plain one, so WCAG 1.4.11 asks 3:1 of it. Against the
931
+ // header fill it gives 3.40 light and 4.22 dark; at 0.4 it was 2.13
932
+ // and 2.73.
933
+ opacity: 0.6;
934
+ }
935
+ }
936
+ th.sorted-asc > a[href*="order="]:after {
937
+ border-top-width: 0;
938
+ border-bottom-width: 4px;
939
+ border-top-color: transparent;
940
+ border-bottom-color: currentColor;
941
+ opacity: 1;
942
+ }
943
+ th.sorted-desc > a[href*="order="]:after { opacity: 1; }
944
+ // Right edge = a single 1px line on the last-column cells (header th, body
945
+ // td, footer cells) coloured like the table border, since the table itself
946
+ // no longer draws a right border.
947
+ thead th:last-child,
948
+ tbody td:last-child,
949
+ tfoot tr > :last-child {
950
+ border-right: 1px solid var(--aa-border);
951
+ }
952
+ // Drop the bottom row's cell border: with border-collapse: separate it sits
953
+ // just inside the table's own bottom border, so the two stack and the bottom
954
+ // edge looks thicker than the other sides. The bottom row is the footer's
955
+ // last row when there's a totals footer, otherwise the body's last row.
956
+ tfoot tr:last-child > td,
957
+ tfoot tr:last-child > th { border-bottom: 0; }
958
+ &:not(:has(tfoot)) tbody tr:last-child > td { border-bottom: 0; }
959
+ // Round the corner cells so their square backgrounds follow the table's
960
+ // rounded border instead of cutting square notches into it (separate borders
961
+ // don't round cell backgrounds). Nest 1px inside the table's outer radius.
962
+ thead th:first-child { border-top-left-radius: inner-radius($skinBorderRadius); }
963
+ thead th:last-child { border-top-right-radius: inner-radius($skinBorderRadius); }
964
+ tfoot tr:last-child > :first-child { border-bottom-left-radius: inner-radius($skinBorderRadius); }
965
+ tfoot tr:last-child > :last-child { border-bottom-right-radius: inner-radius($skinBorderRadius); }
966
+ &:not(:has(tfoot)) tbody tr:last-child > td:first-child { border-bottom-left-radius: inner-radius($skinBorderRadius); }
967
+ &:not(:has(tfoot)) tbody tr:last-child > td:last-child { border-bottom-right-radius: inner-radius($skinBorderRadius); }
369
968
  th.col-selectable {
370
969
  width: 12px;
371
970
  }
971
+ // Center the batch-actions checkboxes (header select-all + per-row): they are
972
+ // baseline-aligned and otherwise ride a few px high in the cell.
973
+ .col-selectable input[type="checkbox"] {
974
+ vertical-align: middle;
975
+ }
372
976
  th.sorted-desc {
373
977
  background-color: $skinBorderWindowColor;
374
978
  }
@@ -388,18 +992,19 @@ body.active_admin {
388
992
  .sidebar_section.panel {
389
993
  background-image: none;
390
994
  box-shadow: none;
391
- background-color: #ffffff;
392
- border: 1px solid $skinBorderWindowColor;
393
- border-bottom: 2px solid $skinBorderWindowColor;
995
+ background-color: var(--aa-surface);
996
+ // Uniform 1px border (no thicker bottom) so the rounded corners are even.
997
+ border: 1px solid var(--aa-border);
394
998
  & > h3 {
395
999
  background-image: none;
396
1000
  box-shadow: none;
397
1001
  text-shadow: none;
398
- background-color: $skinMainSecondColor;
399
- color: #ffffff;
1002
+ background-color: var(--aa-panel-header);
1003
+ color: var(--aa-panel-header-text);
400
1004
  border: none;
401
- padding: 8px 15px;
402
- font-weight: 400;
1005
+ border-bottom: 1px solid var(--aa-border);
1006
+ padding: $skinPanelHeaderPaddingY 15px;
1007
+ font-weight: 700; // match the tab label weight
403
1008
 
404
1009
  -webkit-border-top-left-radius: $skinBorderRadius;
405
1010
  -webkit-border-top-right-radius: $skinBorderRadius;
@@ -410,11 +1015,11 @@ body.active_admin {
410
1015
  }
411
1016
  .filter_form_field {
412
1017
  label {
413
- color: #8494a8;
1018
+ color: var(--aa-label-text);
414
1019
  }
415
1020
  input {
416
- border: 1px solid #e6e9ee;
417
- background-color: #fdfefe;
1021
+ border: 1px solid var(--aa-input-border);
1022
+ background-color: var(--aa-input-bg);
418
1023
  }
419
1024
  input:focus {
420
1025
  border-color: lighten( $skinMainSecondColor, 20% ) ;
@@ -429,18 +1034,18 @@ form fieldset > ol > li fieldset.fragments ol {
429
1034
  form fieldset ol > li.has_many_container h3 {
430
1035
  color: #8494a8;
431
1036
  }
432
- form input[type=submit], a.button, a:link.button, a:visited.button, fieldset.actions > ol > li.button_action > button {
1037
+ form input[type=submit], form input[type=button], form button, a.button, a:link.button, a:visited.button {
433
1038
  background-image: none;
434
1039
  box-shadow: none;
435
1040
  text-shadow: none;
436
- background-color: $skinMainSecondColor;
1041
+ background-color: var(--aa-button);
437
1042
  @include rounded($skinBorderRadius);
438
1043
  border: none;
439
1044
  padding: 10px 20px;
440
- color: #ffffff;
1045
+ color: var(--aa-button-text);
441
1046
  &:hover {
442
1047
  background-image: none!important;
443
- background-color: lighten( $skinMainSecondColor, 5% )!important;
1048
+ background-color: var(--aa-button-hover)!important;
444
1049
  }
445
1050
  }
446
1051
  form fieldset.buttons li.cancel a, form fieldset.actions li.cancel a, form.filter_form a.clear_filters_btn {
@@ -461,20 +1066,21 @@ form fieldset.inputs {
461
1066
  box-shadow: none;
462
1067
  text-shadow: none;
463
1068
  box-shadow: none;
464
- background-color: #ffffff;
465
- border: 1px solid #e6e9ee;
466
- border-bottom: 2px solid #e6e9ee;
1069
+ background-color: var(--aa-surface);
1070
+ // Uniform 1px border (no thicker bottom) so the rounded corners stay even.
1071
+ border: 1px solid var(--aa-input-border);
467
1072
  legend {
468
1073
  span {
469
1074
  background-image: none;
470
1075
  box-shadow: none;
471
1076
  text-shadow: none;
472
1077
  box-shadow: none;
473
- background-color: $skinMainSecondColor;
474
- color: #ffffff;
1078
+ background-color: var(--aa-panel-header);
1079
+ color: var(--aa-panel-header-text);
475
1080
  border: none;
476
- padding: 8px 15px;
477
- font-weight: 400;
1081
+ border-bottom: 1px solid var(--aa-border);
1082
+ padding: $skinPanelHeaderPaddingY 15px;
1083
+ font-weight: 700; // match the tab label weight
478
1084
 
479
1085
  -webkit-border-top-left-radius: $skinBorderRadius;
480
1086
  -webkit-border-top-right-radius: $skinBorderRadius;
@@ -486,11 +1092,11 @@ form fieldset.inputs {
486
1092
  }
487
1093
  ol > li {
488
1094
  label {
489
- color: #8494a8;
1095
+ color: var(--aa-label-text);
490
1096
  }
491
1097
  input, textarea {
492
- border: 1px solid #e6e9ee;
493
- background-color: #fdfefe;
1098
+ border: 1px solid var(--aa-input-border);
1099
+ background-color: var(--aa-input-bg);
494
1100
  }
495
1101
  input:focus, textarea:focus {
496
1102
  border-color: lighten( $skinMainSecondColor, 20% ) ;
@@ -521,7 +1127,7 @@ form fieldset.inputs {
521
1127
  border-bottom: 10px solid $skinMainSecondColor;
522
1128
  }
523
1129
  table.ui-datepicker-calendar {
524
- background-color: #ffffff;
1130
+ background-color: var(--aa-surface);
525
1131
  border-color: $skinMainSecondColor;
526
1132
  thead {
527
1133
  tr {
@@ -551,20 +1157,24 @@ form fieldset.inputs {
551
1157
  box-shadow: none;
552
1158
  text-shadow: none;
553
1159
  box-shadow: none;
554
- background-color: #ffffff;
555
- border: 1px solid $skinBorderWindowColor;
556
- border-bottom: 2px solid $skinBorderWindowColor;
1160
+ background-color: var(--aa-surface);
1161
+ // Uniform border width on all sides: a thicker (2px) bottom border combined
1162
+ // with border-radius renders a distorted, non-uniform bottom corner (the outer
1163
+ // edge curves but the border's inner edge looks square). 1px all round keeps
1164
+ // the rounded corners symmetric.
1165
+ border: 1px solid var(--aa-border);
557
1166
  > h3 {
558
1167
  background-image: none;
559
1168
  box-shadow: none;
560
1169
  text-shadow: none;
561
1170
  box-shadow: none;
562
- background-color: $skinMainSecondColor;
563
- color: #ffffff;
1171
+ background-color: var(--aa-panel-header);
1172
+ color: var(--aa-panel-header-text);
564
1173
  border: none;
565
- padding: 8px 15px;
1174
+ border-bottom: 1px solid var(--aa-border);
1175
+ padding: $skinPanelHeaderPaddingY 15px;
566
1176
  margin-top: 0;
567
- font-weight: 400;
1177
+ font-weight: 700; // match the tab label weight
568
1178
 
569
1179
  -webkit-border-top-left-radius: $skinBorderRadius;
570
1180
  -webkit-border-top-right-radius: $skinBorderRadius;
@@ -581,11 +1191,46 @@ form fieldset.inputs {
581
1191
  padding: 10px 0;
582
1192
  }
583
1193
  .attributes_table table th {
584
- color: #8494a8;
1194
+ color: var(--aa-label-text);
1195
+ // base AA gives attribute labels an emboss text-shadow, which reads as a
1196
+ // blurry halo. Drop it (the index table headers are already shadow-free).
1197
+ text-shadow: none;
1198
+ }
1199
+ // Index-table column headers: one color for both the sortable <a> links and the
1200
+ // plain headers (configurable via --aa-table-header-text). !important overrides
1201
+ // the dark-mode content-link recolor that would otherwise turn sortable headers
1202
+ // blue.
1203
+ table.index_table thead th,
1204
+ table.index_table thead th a {
1205
+ color: var(--aa-table-header-text) !important;
585
1206
  }
1207
+
1208
+ // Form section header: a <legend> interrupts the fieldset's top border, so the
1209
+ // header bar has no border around it (unlike a panel header). Float the legend
1210
+ // so it flows as a normal block and the fieldset border wraps around it.
1211
+ // padding-top: 0 so the header sits flush against the top border (the fieldset's
1212
+ // 10px top padding would otherwise leave a gap above the floated legend).
1213
+ form fieldset.inputs { padding-top: 0; }
1214
+ form fieldset.inputs > legend { float: left; width: 100%; }
1215
+ form fieldset.inputs > ol { clear: both; }
586
1216
  .comments form.active_admin_comment fieldset.inputs {
587
1217
  border: none
588
1218
  }
1219
+ // Comment meta column (author / date / "Delete Comment"). AA's base _comments.scss
1220
+ // caps it at 130px — too narrow for the timestamp (~99px) and the "Delete Comment"
1221
+ // link (~77px) to share a line, so the link wraps to its own row. Widen the column
1222
+ // so date + link sit together; keep the body/form offset (base = meta width + 20px)
1223
+ // in step.
1224
+ .comments .active_admin_comment {
1225
+ .active_admin_comment_meta { width: 200px; }
1226
+ .active_admin_comment_body { margin-left: 220px; }
1227
+ }
1228
+ .comments form.active_admin_comment { margin-left: 220px; }
1229
+ // AA base sets `.pagination_information b { color: #5c6469 }` globally (the
1230
+ // "Displaying 1 comment" line) — black-on-dark in dark mode. Re-point to the
1231
+ // themed text color so it reads in both modes.
1232
+ .pagination_information,
1233
+ .pagination_information b { color: var(--aa-text); }
589
1234
  .pagination {
590
1235
  span > a {
591
1236
  background-image: none;
@@ -625,20 +1270,26 @@ body.logged_out #content_wrapper #active_admin_content {
625
1270
  box-shadow: none;
626
1271
  @include rounded($skinBorderRadius);
627
1272
 
628
- background-color: #ffffff;
629
- border: 1px solid $skinBorderWindowColor;
630
- border-bottom: 2px solid $skinBorderWindowColor;
1273
+ // The login page uses #content_wrapper (not #wrapper), so the aa-content dark
1274
+ // rules don't reach it — point the box at the palette vars directly so it
1275
+ // follows light/dark like everything else.
1276
+ background-color: var(--aa-surface);
1277
+ color: var(--aa-text);
1278
+ border: 1px solid var(--aa-border);
631
1279
  #login {
632
1280
  h2 {
633
1281
  background-image: none;
634
1282
  box-shadow: none;
635
1283
  text-shadow: none;
636
1284
  box-shadow: none;
637
- background-color: $skinMainSecondColor;
638
- color: #ffffff;
1285
+ // Match the regular panel header: receding header color, header text color,
1286
+ // bottom outline and header height — all from the same vars.
1287
+ background-color: var(--aa-panel-header);
1288
+ color: var(--aa-panel-header-text);
639
1289
  border: none;
640
- padding: 8px 15px;
641
- font-weight: 400;
1290
+ border-bottom: 1px solid var(--aa-border);
1291
+ padding: $skinPanelHeaderPaddingY 15px;
1292
+ font-weight: 700; // match the tab label weight
642
1293
 
643
1294
  -webkit-border-top-left-radius: $skinBorderRadius;
644
1295
  -webkit-border-top-right-radius: $skinBorderRadius;
@@ -654,7 +1305,7 @@ body.logged_out #content_wrapper #active_admin_content {
654
1305
  ol {
655
1306
  li {
656
1307
  label {
657
- color: #8494a8;
1308
+ color: var(--aa-label-text);
658
1309
  }
659
1310
  }
660
1311
  }
@@ -663,8 +1314,22 @@ body.logged_out #content_wrapper #active_admin_content {
663
1314
  }
664
1315
  }
665
1316
  form input[type=text], form input[type=password], form input[type=email], form input[type=number], form input[type=url], form input[type=tel], form textarea {
666
- border: 1px solid #e6e9ee;
667
- background-color: #fdfefe;
1317
+ border: 1px solid var(--aa-input-border);
1318
+ background-color: var(--aa-input-bg);
1319
+ color: var(--aa-text);
1320
+ }
1321
+ // Flat form controls: strip the base ActiveAdmin emboss (white gradient fill,
1322
+ // white emboss text-shadow, inset light box-shadow) from text inputs, selects
1323
+ // and textareas in BOTH modes. Keeping this mode-agnostic means dark mode only
1324
+ // has to recolor the controls via the --aa-* vars (no extra dark-only resets).
1325
+ #active_admin_content {
1326
+ input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]):not([type=file]):not([type=image]),
1327
+ textarea,
1328
+ select {
1329
+ background-image: none;
1330
+ text-shadow: none;
1331
+ box-shadow: none;
1332
+ }
668
1333
  }
669
1334
  form fieldset > ol > li fieldset ol li {
670
1335
  clear: both;
@@ -672,23 +1337,40 @@ form fieldset > ol > li fieldset ol li {
672
1337
  margin-top: 6px
673
1338
  }
674
1339
  }
1340
+ // Dropdown (batch-actions / title-bar / table-tools) menu items: keep them on the
1341
+ // surface text colour, not the global link blue that otherwise leaks in.
1342
+ .dropdown_menu_list li a { color: var(--aa-text) !important; }
1343
+ // The list itself and its row dividers were never repainted — ActiveAdmin
1344
+ // leaves them #FFF and #ebebeb, which in dark mode is a white panel behind
1345
+ // the items and a bright hairline between every one of them. Same selector
1346
+ // depth as the base rules, so source order decides and this comes later.
1347
+ .dropdown_menu .dropdown_menu_list_wrapper .dropdown_menu_list {
1348
+ background-color: var(--aa-elevated);
1349
+ border-color: var(--aa-border);
1350
+ }
1351
+ .dropdown_menu .dropdown_menu_list_wrapper .dropdown_menu_list li {
1352
+ border-bottom-color: var(--aa-border);
1353
+ }
1354
+
675
1355
  .ui-dialog {
676
1356
  background-image: none;
677
1357
  box-shadow: none;
678
1358
  text-shadow: none;
679
- box-shadow: none;
680
- background-color: #ffffff;
681
- border-bottom: 2px solid $skinBorderWindowColor;
1359
+ background-color: var(--aa-surface);
1360
+ color: var(--aa-text);
1361
+ border-bottom: 1px solid var(--aa-border);
682
1362
  .ui-dialog-titlebar {
683
1363
  background-image: none;
684
1364
  box-shadow: none;
685
1365
  text-shadow: none;
686
1366
  box-shadow: none;
687
- background-color: $skinMainSecondColor;
688
- color: #ffffff;
1367
+ background-color: var(--aa-panel-header);
1368
+ color: var(--aa-panel-header-text);
689
1369
  border: none;
690
- padding: 8px 15px;
691
- font-weight: 400;
1370
+ border-bottom: 1px solid var(--aa-border);
1371
+ padding: $skinPanelHeaderPaddingY 15px;
1372
+ font-weight: 700;
1373
+ font-size: 1em;
692
1374
 
693
1375
  -webkit-border-top-left-radius: $skinBorderRadius;
694
1376
  -webkit-border-top-right-radius: $skinBorderRadius;
@@ -697,6 +1379,15 @@ form fieldset > ol > li fieldset ol li {
697
1379
  border-top-left-radius: $skinBorderRadius;
698
1380
  border-top-right-radius: $skinBorderRadius;
699
1381
  }
1382
+ // jQuery-UI close button: drop the default grey gradient pill so it blends into
1383
+ // the accent titlebar.
1384
+ .ui-dialog-titlebar-close {
1385
+ background: transparent;
1386
+ background-image: none;
1387
+ border: none;
1388
+ box-shadow: none;
1389
+ color: var(--aa-panel-header-text);
1390
+ }
700
1391
  form {
701
1392
  ul {
702
1393
  li {
@@ -706,8 +1397,8 @@ form fieldset > ol > li fieldset ol li {
706
1397
  }
707
1398
  input[type="text"], input[type="number"], input[type="datepicker"], textarea {
708
1399
  padding: 8px 10px 7px;
709
- border: 1px solid #e6e9ee;
710
- background-color: #fdfefe;
1400
+ border: 1px solid var(--aa-input-border);
1401
+ background-color: var(--aa-input-bg);
711
1402
  outline: none;
712
1403
  @include rounded($skinBorderRadius);
713
1404
  width: 247px;
@@ -727,28 +1418,28 @@ form fieldset > ol > li fieldset ol li {
727
1418
  text-shadow: none;
728
1419
  box-shadow: none;
729
1420
  @include rounded($skinBorderRadius);
730
- background-color: $skinMainSecondColor;
1421
+ background-color: var(--aa-button);
731
1422
  border: none;
732
- color: #ffffff;
1423
+ color: var(--aa-button-text);
733
1424
  font-weight: 400;
734
1425
  padding: 10px 20px;
735
1426
  &:hover {
736
1427
  background-image: none!important;
737
- background-color: lighten( $skinMainSecondColor, 5% )!important;
1428
+ background-color: var(--aa-button-hover)!important;
738
1429
  }
739
1430
  }
740
1431
  button:last-child {
741
1432
  background-image: none;
742
1433
  box-shadow: none;
743
1434
  text-shadow: none;
744
- box-shadow: none;
745
- background-color: #ffffff;
1435
+ background-color: var(--aa-surface);
1436
+ color: var(--aa-text);
746
1437
  @include rounded($skinBorderRadius);
747
1438
  padding: 9px 20px 9px 20px;
748
- border-color: #E4E4E4;
1439
+ border: 1px solid var(--aa-border);
749
1440
  &:hover {
750
1441
  background-image: none!important;
751
- background-color: #F4F4F4!important;
1442
+ background-color: var(--aa-surface-hover)!important;
752
1443
  }
753
1444
  }
754
1445
  }
@@ -783,6 +1474,247 @@ input[type='radio'] {
783
1474
  }
784
1475
  }
785
1476
 
786
- .attributes_table table td .status_tag.empty {
787
- color: #ffffff;
1477
+ // Status tag: solid colour fill with a white label (high visibility), and an
1478
+ // outline a shade darker than the fill so the edge reads in the tag's own
1479
+ // colour. All colours are fixed, so a tag looks identical in light and dark.
1480
+ @mixin status-tag-colors($c) {
1481
+ background: $c;
1482
+ border-color: mix($c, #000000, 84%); // = darken($c) — outline in the fill colour
1483
+ // The fills are dark enough to carry this label: every one of the five is at
1484
+ // least 4.53 against white. Lighten one and the label needs to go with it.
1485
+ color: $skinStatusTagTextColor;
1486
+ }
1487
+
1488
+ //CONTENT (both modes)-----------------------------------------------------------//
1489
+ // Repaint ActiveAdmin's content onto the semantic palette. Included
1490
+ // unconditionally; the --aa-* custom properties carry the per-mode values (light
1491
+ // on :root, dark via aa-dark-palette), so this one rule set themes both modes.
1492
+ // Only the genuinely-dark overrides (see aa-dark-content, below) stay dark-scoped.
1493
+ @mixin aa-content {
1494
+ body.active_admin { color: var(--aa-text); }
1495
+
1496
+ #wrapper #active_admin_content {
1497
+ color: var(--aa-text);
1498
+
1499
+ // Status tags: solid colour fill + white label (high visibility), an
1500
+ // in-colour outline, rounded corners and a centered label. Colours are
1501
+ // mode-independent (same in light and dark). The base ActiveAdmin rule
1502
+ // supplies uppercase + letter-spacing + font-size; text-indent offsets the
1503
+ // trailing letter-spacing so the label is horizontally centered.
1504
+ .status_tag {
1505
+ display: inline-block;
1506
+ line-height: 1;
1507
+ // 4px (not 5px) horizontal padding so the added 1px border doesn't widen
1508
+ // the tag past ActiveAdmin's original borderless 5px-padded width. 1px less
1509
+ // bottom padding than top (like the stock AA tag) optically centers the
1510
+ // uppercase label, which otherwise rides high (no descenders to fill the
1511
+ // reserved space below the baseline).
1512
+ padding: 3px 4px 2px;
1513
+ text-indent: 0.15em;
1514
+ border-radius: 2px;
1515
+ border: 1px solid;
1516
+
1517
+ @include status-tag-colors($skinStatusTagNeutralColor);
1518
+ &.ok, &.published, &.complete, &.completed, &.green, &.yes { @include status-tag-colors($skinStatusTagOkColor); }
1519
+ &.notice, &.blue { @include status-tag-colors($skinStatusTagNoticeColor); }
1520
+ &.warn, &.warning, &.orange { @include status-tag-colors($skinStatusTagWarnColor); }
1521
+ &.error, &.errored, &.red { @include status-tag-colors($skinStatusTagErrorColor); }
1522
+ &.empty, &.unknown, &.none {
1523
+ background: none;
1524
+ border: 0; // placeholder/empty tags stay unobtrusive (no outline)
1525
+ color: var(--aa-text-muted);
1526
+ font-weight: bold;
1527
+ font-size: 0.8em;
1528
+ text-indent: 0;
1529
+ }
1530
+ }
1531
+
1532
+ // Generic content links take the link color. Exclude UI "chrome" links that
1533
+ // carry their own color — buttons, title-bar actions, scope/batch buttons
1534
+ // (.table_tools_button), jQuery-UI tab anchors (.ui-tabs-anchor) — and delete
1535
+ // links (handled by their own rule). Excluding them means those rules win on
1536
+ // their own, with no need for !important.
1537
+ // :where() wraps the exclusions so they filter without adding weight. Bare,
1538
+ // the seven :not()s are worth seven class-level points, and that beat every
1539
+ // component rule declared later — pagination links and the Clear Filters
1540
+ // button came out --aa-link instead of --aa-text.
1541
+ a:where(:not(.button):not(.action_item):not(.table_tools_button):not(.dropdown_menu_button):not(.ui-tabs-anchor):not(.delete_link):not([data-method="delete"])) { color: var(--aa-link); }
1542
+ a[data-method="delete"], a.delete_link { color: var(--aa-delete-link); }
1543
+
1544
+ // Pagination — non-current page chips use a light-grey bg in the theme
1545
+ .pagination span > a { background-color: var(--aa-surface-2); color: var(--aa-text); }
1546
+ // The hovered page link keeps its accent fill, so the label has to be
1547
+ // the colour that reads on the accent — not the link colour. The generic
1548
+ // link rule above scores (2,6,1) thanks to its six :not() clauses and
1549
+ // cannot be out-specified by anything sane here, which is why this needs
1550
+ // !important, like the accent fill one line over in the base pagination
1551
+ // block. Without it the digit renders at 1.00:1 on its own background.
1552
+ .pagination span > a:hover { color: $skinMenuPillTextColor !important; }
1553
+
1554
+ // Batch-actions / table-tools dropdown menu items are white by default
1555
+ .table_tools .dropdown_menu_list li a {
1556
+ background-color: var(--aa-surface);
1557
+ color: var(--aa-text);
1558
+ }
1559
+
1560
+ // NB: do NOT give .panel_contents its own background — it's a square-cornered
1561
+ // child that fills the panel, so a bg on it would paint over the panel's
1562
+ // rounded inner border edge and make the corners look square. The parent
1563
+ // .panel background shows through instead.
1564
+ .panel,
1565
+ .sidebar_section,
1566
+ .dashboard_section,
1567
+ fieldset.inputs {
1568
+ background: var(--aa-surface);
1569
+ border-color: var(--aa-border);
1570
+ color: var(--aa-text);
1571
+ }
1572
+ // .index_content wraps the index table, which already paints its own
1573
+ // (rounded) surface. Giving this square-cornered wrapper a surface bg makes
1574
+ // it show through the table's rounded corners as white squares, so leave it
1575
+ // transparent (same reasoning as .panel_contents above).
1576
+ .index_content { color: var(--aa-text); }
1577
+ // Blank-slate ("No records") container blends into the page background
1578
+ // rather than reading as a panel surface.
1579
+ .blank_slate_container {
1580
+ background: var(--aa-page-bg);
1581
+ color: var(--aa-text);
1582
+ }
1583
+ // Cancel / clear buttons are white in the theme (a light secondary button)
1584
+ fieldset.buttons li.cancel a, fieldset.actions li.cancel a,
1585
+ form.filter_form a.clear_filters_btn {
1586
+ background-color: var(--aa-surface-2);
1587
+ color: var(--aa-text);
1588
+ border-color: var(--aa-input-border);
1589
+ }
1590
+ // (Tab styling — colors, the no-bottom-outline inactive tabs, and the z-index
1591
+ // baseline — lives in the base #wrapper div.tabs block now, so it applies in
1592
+ // both light and dark via the --aa-* vars.)
1593
+
1594
+ table.index_table,
1595
+ .attributes_table table,
1596
+ table {
1597
+ background: var(--aa-surface);
1598
+ color: var(--aa-text);
1599
+ border-color: var(--aa-border);
1600
+ }
1601
+ // Column headers (index AND plain content tables, e.g. the CDR "Attempts"
1602
+ // table) get a distinct header-row shade so they don't blend with the rows.
1603
+ // Plain table headers are otherwise transparent and read like an odd row.
1604
+ table thead th {
1605
+ background: var(--aa-surface-2);
1606
+ }
1607
+ table.index_table thead th { background: var(--aa-table-header); }
1608
+ table.index_table thead th,
1609
+ .attributes_table th {
1610
+ border-color: var(--aa-border);
1611
+ }
1612
+ table th, table td,
1613
+ table.index_table td,
1614
+ .attributes_table td {
1615
+ border-color: var(--aa-border);
1616
+ color: var(--aa-text);
1617
+ }
1618
+ // Attribute-table labels (the field-name column) use the muted label color;
1619
+ // the more specific selector wins over the generic `table th` rule above.
1620
+ .attributes_table th { color: var(--aa-label-text); }
1621
+ table.index_table tbody tr:nth-child(even) > td,
1622
+ table.index_table tbody tr.even > td { background: var(--aa-surface-2); }
1623
+ table.index_table tbody tr:hover > td { background: var(--aa-surface-hover); }
1624
+ // Checked rows: the zebra and hover rules above outrank ActiveAdmin's own
1625
+ // `tr.selected td`, so without this a ticked row looks exactly like an
1626
+ // unticked one. The :hover variant keeps the selection visible while the
1627
+ // pointer is over the row.
1628
+ table.index_table tbody tr.selected > td,
1629
+ table.index_table tbody tr.selected:hover > td { background: var(--aa-selected); }
1630
+ // Plain content tables (e.g. the CDR show "Attempts" table) are striped by the
1631
+ // base CSS with light fills; map the odd/even rows onto the dark surfaces too.
1632
+ table tbody tr.odd > td, table tbody tr.odd > th { background-color: var(--aa-surface); }
1633
+ table tbody tr.even > td, table tbody tr.even > th { background-color: var(--aa-surface-2); }
1634
+ // Plain table headers carry a white emboss text-shadow (subtle on a light bg,
1635
+ // a blurry halo on dark). Drop it — index tables are already shadow-free.
1636
+ table thead th, table thead td { text-shadow: none; }
1637
+ // Footer / totals row (theme gives it a light grey bg, which on the dark
1638
+ // panel reads as a light rectangle poking against the rounded panel border).
1639
+ table.index_table tfoot tr,
1640
+ table.index_table tfoot td,
1641
+ table.index_table tfoot th {
1642
+ background: var(--aa-surface-2);
1643
+ color: var(--aa-text);
1644
+ }
1645
+
1646
+ // Index footer (per-page selector, pagination, "displaying…" info, download
1647
+ // links): lay them out in one left-packed, vertically-centred row. The gem
1648
+ // floats them, so they sat top-aligned and, having different heights, looked
1649
+ // misaligned. A slightly larger font than the gem default (0.85em) too.
1650
+ #index_footer {
1651
+ text-align: left;
1652
+ font-size: 0.95em;
1653
+ .pagination_per_page,
1654
+ .pagination,
1655
+ .pagination_information,
1656
+ .download_links {
1657
+ float: none;
1658
+ display: inline-block;
1659
+ vertical-align: middle;
1660
+ margin: 0 8px 0 0;
1661
+ }
1662
+ }
1663
+
1664
+ // Forms & inputs
1665
+ // :focus is nested rather than written out alongside: the six :not()s count
1666
+ // as six class-level points, so a sibling `input:focus` (one) loses to the
1667
+ // resting border every time and the focus indicator never appears at all.
1668
+ input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]):not([type=file]):not([type=image]),
1669
+ textarea,
1670
+ select {
1671
+ background-color: var(--aa-input-bg);
1672
+ border-color: var(--aa-input-border);
1673
+ color: var(--aa-text);
1674
+
1675
+ &:focus { border-color: var(--aa-accent); }
1676
+ }
1677
+ input::placeholder, textarea::placeholder { color: var(--aa-text-muted); }
1678
+ option, optgroup { background-color: var(--aa-input-bg); color: var(--aa-text); }
1679
+ label, fieldset > legend, .fieldset > legend { color: var(--aa-label-text); }
1680
+ .inline-hints, .inline_hints, p.inline-hints { color: var(--aa-text-muted); }
1681
+ }
1682
+
1683
+ // jQuery UI datepicker (appended to <body>, so styled at the mixin level)
1684
+ // #ui-datepicker-div, not .ui-datepicker.ui-widget: both ActiveAdmin and the
1685
+ // block higher up this file address it by id, and an id beats any number of
1686
+ // classes — the class-only form here never applied, leaving the calendar on
1687
+ // its light fills with #666 day numbers on the dark surface (2.61:1).
1688
+ #ui-datepicker-div.ui-datepicker {
1689
+ background: var(--aa-surface);
1690
+ border-color: var(--aa-border);
1691
+ color: var(--aa-text);
1692
+ // The header keeps the accent fill and its white label in both modes — the
1693
+ // theme paints it that way above, and it reads on either background. Only
1694
+ // the calendar body needs repointing; a blanket `a` here would turn the
1695
+ // header's month arrows and title dark on the accent.
1696
+ border-color: var(--aa-border);
1697
+ table.ui-datepicker-calendar { background-color: var(--aa-surface); }
1698
+ table.ui-datepicker-calendar thead th { background-color: var(--aa-surface-2); color: var(--aa-text-muted); }
1699
+ table.ui-datepicker-calendar tbody td a { color: var(--aa-text); }
1700
+ table.ui-datepicker-calendar tbody td span { color: var(--aa-text-muted); }
1701
+ .ui-state-hover, .ui-state-active { background: var(--aa-surface-hover); }
1702
+ }
1703
+ }
1704
+
1705
+ // DARK-ONLY content overrides — genuinely dark-specific values with no light
1706
+ // equivalent (or that would be wrong on a light surface), so these stay scoped
1707
+ // to dark mode while everything in aa-content is mode-agnostic.
1708
+ @mixin aa-dark-content {
1709
+ // (Currently empty — status tags now use mode-independent soft colours in
1710
+ // aa-content, so no dark-only override is needed. Kept for future dark-only
1711
+ // content rules.)
1712
+ }
1713
+
1714
+ // Content styling applies in BOTH modes (aa-dark-palette supplies the per-mode
1715
+ // --aa-* values); only the genuinely-dark overrides are dark-scoped.
1716
+ @include aa-content;
1717
+ @media (prefers-color-scheme: dark) {
1718
+ html:not([data-theme="light"]) { @include aa-dark-content; }
788
1719
  }
1720
+ html[data-theme="dark"] { @include aa-dark-content; }