active_admin_theme 3.1.1 → 3.2.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.
- checksums.yaml +4 -4
- data/README.md +185 -34
- data/app/assets/javascripts/wigu/all.js +10 -0
- data/app/assets/javascripts/wigu/scrollbar_width.js +72 -0
- data/app/assets/javascripts/wigu/server_clock.js +171 -0
- data/app/assets/javascripts/wigu/table_crisscross.js +49 -0
- data/app/assets/stylesheets/wigu/active_admin_theme.scss +473 -12
- data/lib/active_admin_theme/version.rb +1 -1
- data/lib/active_admin_theme.rb +127 -1
- metadata +5 -1
|
@@ -55,6 +55,9 @@ $skinTitleBarBorderColor: $skinMainSecondColor!default;
|
|
|
55
55
|
$skinTitleBarBorderWidth: 0!default;
|
|
56
56
|
$skinTitleBarButtonPaddingY: 6px!default; // action button vertical padding
|
|
57
57
|
$skinTitleBarButtonPaddingX: 10px!default; // action button horizontal padding
|
|
58
|
+
// Minimum bar height — raise it alongside the button paddings above if your
|
|
59
|
+
// buttons outgrow the default band (the bar still grows to fit its content).
|
|
60
|
+
$skinTitleBarHeight: 37px!default;
|
|
58
61
|
|
|
59
62
|
// Panel/section headers + tabs accent (the blue bar on panel headers and the
|
|
60
63
|
// inactive tabs). $skinPanelHeaderColor is the base/light value;
|
|
@@ -214,6 +217,20 @@ $skinStatusTagErrorColor: #ce483b!default; // error / errored / red
|
|
|
214
217
|
--aa-active-tab-text: #{$skinActiveTabTextColor};
|
|
215
218
|
--aa-inactive-tab-text: #{$skinInactiveTabTextColor};
|
|
216
219
|
--aa-delete-link: #{$skinDeleteLinkColor};
|
|
220
|
+
|
|
221
|
+
// Bridge for the companion active_admin_datetimepicker plugin: feed its
|
|
222
|
+
// --aa-datetimepicker-* inputs from this theme's palette, so the xdsoft picker
|
|
223
|
+
// is themed (light + dark) automatically with no per-app override. These point
|
|
224
|
+
// at the --aa-* vars, which are themselves redefined per mode, so a single set
|
|
225
|
+
// here covers both. Harmless no-op when the plugin isn't installed (nothing
|
|
226
|
+
// reads these). A host can still override any of them downstream.
|
|
227
|
+
--aa-datetimepicker-accent: var(--aa-accent);
|
|
228
|
+
--aa-datetimepicker-surface: var(--aa-surface);
|
|
229
|
+
--aa-datetimepicker-surface-2: var(--aa-surface-2);
|
|
230
|
+
--aa-datetimepicker-text: var(--aa-text);
|
|
231
|
+
--aa-datetimepicker-text-muted: var(--aa-text-muted);
|
|
232
|
+
--aa-datetimepicker-border: var(--aa-border);
|
|
233
|
+
--aa-datetimepicker-hover: var(--aa-surface-hover);
|
|
217
234
|
}
|
|
218
235
|
// OS preference, unless the user explicitly chose light
|
|
219
236
|
@media (prefers-color-scheme: dark) {
|
|
@@ -249,6 +266,11 @@ $theme-icon-auto: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/s
|
|
|
249
266
|
$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;
|
|
250
267
|
$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;
|
|
251
268
|
|
|
269
|
+
// Logout drawn as a sign-out glyph instead of the word "Logout", so it matches
|
|
270
|
+
// the icon toggle beside it. Masked like the theme icons above, so it takes
|
|
271
|
+
// currentColor and follows $skinMenuTextColor; a consumer can override the var.
|
|
272
|
+
$logout-icon: 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' stroke-linejoin='round'%3E%3Cpath d='M6.5 2.5H3.2v11h3.3'/%3E%3Cpath d='M12.8 8H6'/%3E%3Cpath d='M9.5 5 12.8 8l-3.3 3'/%3E%3C/svg%3E")!default;
|
|
273
|
+
|
|
252
274
|
// A wrong-typed override is legal SassScript, so it compiles without a word and
|
|
253
275
|
// emits CSS the browser drops — `border-bottom: none solid #5ea3d3`,
|
|
254
276
|
// `padding: 10 12px`. The rule silently vanishes and ActiveAdmin's own value
|
|
@@ -361,10 +383,35 @@ $theme-icon-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/s
|
|
|
361
383
|
html {
|
|
362
384
|
background: var(--aa-page-bg);
|
|
363
385
|
}
|
|
386
|
+
|
|
387
|
+
// Pin a bar (#header, #title_bar) to the viewport's left edge at the visible
|
|
388
|
+
// viewport width while a wide index table scrolls the whole page sideways
|
|
389
|
+
// (#wrapper is display:table, min-width:100%). width = 100vw minus the vertical
|
|
390
|
+
// scrollbar — the JS-set --scrollbar-width var (0 when wigu/scrollbar_width is
|
|
391
|
+
// absent, and then max-width:100% keeps 100vw's scrollbar allowance from adding a
|
|
392
|
+
// stray horizontal scrollbar). It's a plain max-width, not min(…, 100%):
|
|
393
|
+
// sassc/libsass evaluates the Sass min() function and rejects calc() as a
|
|
394
|
+
// non-number. No `top`, so the bar still scrolls vertically.
|
|
395
|
+
@mixin viewport-pinned-bar {
|
|
396
|
+
position: sticky;
|
|
397
|
+
left: 0;
|
|
398
|
+
width: calc(100vw - var(--scrollbar-width, 0px));
|
|
399
|
+
max-width: 100%;
|
|
400
|
+
box-sizing: border-box;
|
|
401
|
+
}
|
|
402
|
+
|
|
364
403
|
body.active_admin {
|
|
365
404
|
background: var(--aa-page-bg);
|
|
366
405
|
}
|
|
367
406
|
#wrapper {
|
|
407
|
+
// ActiveAdmin lays #wrapper out as `display: table`, which sizes to its
|
|
408
|
+
// content: a wide index table therefore scrolls the WHOLE page horizontally
|
|
409
|
+
// (the master / pre-theme behaviour admins are used to). Only guarantee the
|
|
410
|
+
// layout fills the viewport when the content is narrow. The utility nav
|
|
411
|
+
// (clock / theme toggle / logout) is pinned `sticky` (see #utility_nav below)
|
|
412
|
+
// so it stays reachable while the page scrolls sideways.
|
|
413
|
+
min-width: 100%;
|
|
414
|
+
|
|
368
415
|
div.tabs {
|
|
369
416
|
> ul > li > a {
|
|
370
417
|
background-image: none;
|
|
@@ -427,6 +474,12 @@ body.active_admin {
|
|
|
427
474
|
border: none;
|
|
428
475
|
text-shadow: none;
|
|
429
476
|
background-image: none;
|
|
477
|
+
|
|
478
|
+
// A wide index table scrolls the WHOLE page sideways. Pin the header on
|
|
479
|
+
// screen at the viewport width so it doesn't stretch to the table's width
|
|
480
|
+
// (which spread the menu out under the utility nav and overlapped it); it
|
|
481
|
+
// still grows to fit a wrapped menu, ActiveAdmin's natural layout.
|
|
482
|
+
@include viewport-pinned-bar;
|
|
430
483
|
h1.site_title {
|
|
431
484
|
color: $skinMainSecondColor;
|
|
432
485
|
font-size: 16px;
|
|
@@ -457,10 +510,34 @@ body.active_admin {
|
|
|
457
510
|
// tabs: drop the pill padding, and on hover highlight by text colour only — no
|
|
458
511
|
// background block. The clock has no click action, so it gets no hover at all.
|
|
459
512
|
#utility_nav {
|
|
513
|
+
// Sits at the top-right of the header; the header itself is pinned on screen
|
|
514
|
+
// (sticky, see #header above) so the clock / theme toggle / logout stay
|
|
515
|
+
// reachable while a wide table scrolls the page sideways.
|
|
516
|
+
z-index: 2;
|
|
517
|
+
white-space: nowrap;
|
|
518
|
+
|
|
460
519
|
> li > a { padding: 0 5px; }
|
|
461
520
|
> li:hover > a, > li.current > a { background-color: transparent; }
|
|
462
521
|
> li#servertime:hover > a { color: #ffffff; }
|
|
463
522
|
|
|
523
|
+
// Server clock (server_clock.js), rendered as two compact lines like
|
|
524
|
+
// yeti-web: time + timezone (larger) over year/month/day (smaller). A
|
|
525
|
+
// right-aligned flex column sized to the nav band (~24px) so both lines
|
|
526
|
+
// fit the header height unchanged. Monospace + tabular figures keep the
|
|
527
|
+
// ticking time a constant width, so the adjacent logout doesn't jitter.
|
|
528
|
+
> li#servertime > span {
|
|
529
|
+
display: inline-flex;
|
|
530
|
+
flex-direction: column;
|
|
531
|
+
justify-content: center;
|
|
532
|
+
align-items: flex-end;
|
|
533
|
+
vertical-align: middle;
|
|
534
|
+
height: 24px;
|
|
535
|
+
font-family: 'SFMono-Regular', Menlo, Consolas, 'Liberation Mono', monospace;
|
|
536
|
+
font-variant-numeric: tabular-nums;
|
|
537
|
+
}
|
|
538
|
+
> li#servertime .clock-time { font-size: 12px; line-height: 1.1; }
|
|
539
|
+
> li#servertime .clock-date { font-size: 9px; line-height: 1.1; }
|
|
540
|
+
|
|
464
541
|
// The theme switch (app/assets/javascripts/wigu/theme_toggle.js). The
|
|
465
542
|
// script only writes data-mode; the glyph is drawn here, so a project can
|
|
466
543
|
// swap it for its own icon font without forking the script. Masked rather
|
|
@@ -497,6 +574,35 @@ body.active_admin {
|
|
|
497
574
|
-webkit-mask-image: $theme-icon-dark;
|
|
498
575
|
mask-image: $theme-icon-dark;
|
|
499
576
|
}
|
|
577
|
+
|
|
578
|
+
// Logout as an icon ($logout-icon), to match the toggle beside it.
|
|
579
|
+
// font-size:0 hides the "Logout" text visually; the accessible name is
|
|
580
|
+
// the aria-label the gem's Ruby puts on the anchor, because zero-sized
|
|
581
|
+
// text is pruned from the accessibility tree by some screen readers
|
|
582
|
+
// (the toggle gets its aria-label from theme_toggle.js the same way).
|
|
583
|
+
// The masked glyph follows the bar's text colour. The real logout
|
|
584
|
+
// link/method from add_logout_button_to_menu is untouched.
|
|
585
|
+
// Scoped to a.wigu-logout — the class the gem's Ruby puts on the item
|
|
586
|
+
// it adds — so a consumer-rendered logout keeps its own label instead
|
|
587
|
+
// of collapsing to an unlabeled glyph.
|
|
588
|
+
> li#logout:has(> a.wigu-logout) { display: inline-flex; align-items: center; vertical-align: middle; }
|
|
589
|
+
> li#logout > a.wigu-logout {
|
|
590
|
+
display: flex;
|
|
591
|
+
align-items: center;
|
|
592
|
+
font-size: 0;
|
|
593
|
+
opacity: 0.75;
|
|
594
|
+
|
|
595
|
+
&:before {
|
|
596
|
+
content: "";
|
|
597
|
+
display: block;
|
|
598
|
+
width: 16px;
|
|
599
|
+
height: 16px;
|
|
600
|
+
background-color: currentColor;
|
|
601
|
+
-webkit-mask: #{$logout-icon} center / contain no-repeat;
|
|
602
|
+
mask: #{$logout-icon} center / contain no-repeat;
|
|
603
|
+
}
|
|
604
|
+
&:hover, &:focus { opacity: 1; }
|
|
605
|
+
}
|
|
500
606
|
}
|
|
501
607
|
ul.tabs {
|
|
502
608
|
li.has_nested {
|
|
@@ -566,26 +672,87 @@ body.active_admin {
|
|
|
566
672
|
background-color: $skinTitleBarColor;
|
|
567
673
|
border: none;
|
|
568
674
|
border-bottom: $skinTitleBarBorderWidth solid $skinTitleBarBorderColor;
|
|
675
|
+
// Pin the whole bar to the viewport like #header, so the title AND the action
|
|
676
|
+
// buttons stay visible while a wide index table scrolls the page sideways.
|
|
677
|
+
@include viewport-pinned-bar;
|
|
678
|
+
// Lay the title (left) and action items (right) on one compact row. Without
|
|
679
|
+
// this, resetting #titlebar_right to static (below) stacks it under the
|
|
680
|
+
// title and the bar grows tall. min-height, NOT height: with a fixed height
|
|
681
|
+
// the bar cannot contain its content — action items that wrap on a narrow
|
|
682
|
+
// window would overflow it and paint over the page — while min-height still
|
|
683
|
+
// makes breadcrumb (detail) pages match plain index pages. Drop the base
|
|
684
|
+
// clearfix — in a flex container it becomes a stray third item that
|
|
685
|
+
// space-between strands right.
|
|
686
|
+
display: flex;
|
|
687
|
+
align-items: center;
|
|
688
|
+
justify-content: space-between;
|
|
689
|
+
min-height: $skinTitleBarHeight;
|
|
690
|
+
&::after { display: none; }
|
|
569
691
|
#titlebar_left {
|
|
692
|
+
display: flex;
|
|
693
|
+
align-items: center;
|
|
694
|
+
gap: 6px;
|
|
695
|
+
height: auto;
|
|
696
|
+
// Let the title shrink below its min-content width: flex items refuse to
|
|
697
|
+
// otherwise, and a long nowrap record title would push the action items
|
|
698
|
+
// out of the pinned bar — unreachable past the viewport on wide pages.
|
|
699
|
+
min-width: 0;
|
|
570
700
|
span.breadcrumb {
|
|
571
701
|
font-size: 12px;
|
|
572
702
|
text-shadow: none;
|
|
573
|
-
margin-bottom:
|
|
703
|
+
margin-bottom: 0;
|
|
574
704
|
a, span {
|
|
575
|
-
color: lighten( $skinMainFirstColor, 30% )!important;
|
|
705
|
+
color: lighten( $skinMainFirstColor, 30% )!important;
|
|
576
706
|
}
|
|
577
707
|
}
|
|
708
|
+
// Own a sensible title size so consumer apps don't each shrink AA's large
|
|
709
|
+
// default (which towered over the breadcrumb). margin:0 + the flex parent
|
|
710
|
+
// centre it on the breadcrumb's line; nowrap + ellipsis keep a long
|
|
711
|
+
// record title (show pages) on one line without pushing the action
|
|
712
|
+
// items out of the bar — min-width: 0 lets it actually shrink.
|
|
578
713
|
h2 {
|
|
579
714
|
color: #ffffff;
|
|
580
715
|
text-shadow: none;
|
|
716
|
+
font-size: 16px;
|
|
717
|
+
margin: 0;
|
|
718
|
+
min-width: 0;
|
|
719
|
+
// Screen only: AA registers the stylesheet with media "all", and paper
|
|
720
|
+
// has no horizontal scroll — a nowrap/ellipsis title would print
|
|
721
|
+
// truncated at the page edge where it used to wrap.
|
|
722
|
+
@media screen {
|
|
723
|
+
white-space: nowrap;
|
|
724
|
+
overflow: hidden;
|
|
725
|
+
text-overflow: ellipsis;
|
|
726
|
+
}
|
|
581
727
|
}
|
|
582
728
|
}
|
|
583
729
|
#titlebar_right {
|
|
730
|
+
// Horizontal pinning is handled by #title_bar (sticky + viewport width),
|
|
731
|
+
// so the action items just sit at the right of the bar. Reset any
|
|
732
|
+
// consumer `position: absolute/sticky` (each app used to pin the buttons
|
|
733
|
+
// itself via local stylesheet overrides).
|
|
734
|
+
position: static;
|
|
735
|
+
// Shrink to the action-items height (some apps/base leave it ~50px, taller
|
|
736
|
+
// than the 37px bar, so centering pushed it over the top edge).
|
|
737
|
+
height: auto;
|
|
738
|
+
// Space multiple action items apart (e.g. a Menu dropdown next to a New
|
|
739
|
+
// button); the base leaves them flush on this side.
|
|
740
|
+
.action_items > .action_item:not(:last-child):not(:empty) { margin-right: 10px; }
|
|
741
|
+
// The form selectors cover button_to action items: Rails renders them as
|
|
742
|
+
// form > input[type=submit] (or button), which the anchor selectors
|
|
743
|
+
// miss — consumer apps were re-hardcoding the 6px/10px padding to
|
|
744
|
+
// compensate, desyncing from the $skin* knobs on every change.
|
|
584
745
|
// `> a` and `> .dropdown_menu > a`: an action item may hold a dropdown, and
|
|
585
746
|
// a descendant selector here paints its menu entries with the button fill
|
|
586
747
|
// too — with !important, so the entry rules below cannot undo it.
|
|
748
|
+
// A button_to action item renders as a block <form>; inline-block
|
|
749
|
+
// keeps it on the bar's single row (consumers used to float it, which
|
|
750
|
+
// collapsed the flex bar's height instead).
|
|
751
|
+
.action_item > form { display: inline-block; }
|
|
587
752
|
.action_item > a,
|
|
588
|
-
.action_item > .dropdown_menu > a
|
|
753
|
+
.action_item > .dropdown_menu > a,
|
|
754
|
+
.action_item > form > input[type="submit"],
|
|
755
|
+
.action_item > form > button {
|
|
589
756
|
background-image: none;
|
|
590
757
|
border: none;
|
|
591
758
|
box-shadow: none;
|
|
@@ -601,7 +768,28 @@ body.active_admin {
|
|
|
601
768
|
}
|
|
602
769
|
}
|
|
603
770
|
div.dropdown_menu {
|
|
771
|
+
// Containing block for the panel below, so `right: 0` means "the
|
|
772
|
+
// trigger's right edge". AA leaves this element unpositioned, which
|
|
773
|
+
// makes the panel resolve against #title_bar (sticky) and park at the
|
|
774
|
+
// bar's far right no matter which action item was clicked. inline-block
|
|
775
|
+
// over AA's `display: inline`: an inline box's containing block spans
|
|
776
|
+
// its text fragments, so the markup's whitespace around the button
|
|
777
|
+
// pushed the panel a few px past the button's edge.
|
|
778
|
+
position: relative;
|
|
779
|
+
display: inline-block;
|
|
604
780
|
div.dropdown_menu_list_wrapper {
|
|
781
|
+
// Always right-align title-bar dropdowns under their own button,
|
|
782
|
+
// overriding AA's inline positioning. AA 3.x's _position() sets
|
|
783
|
+
// inline `left` OR `right` per open depending on a window-geometry
|
|
784
|
+
// branch and never clears the other, and the wrapper persists across
|
|
785
|
+
// opens — after a resize between opens both offsets accumulate and
|
|
786
|
+
// the width:auto wrapper stretches from the stale left coordinate to
|
|
787
|
+
// the right edge. Its `right` branch is wrong here anyway: it offsets
|
|
788
|
+
// from the window, not from the bar. Consumer apps used to carry
|
|
789
|
+
// exactly this pair as a local hotfix; the theme owns the bar now, so
|
|
790
|
+
// it owns the guard.
|
|
791
|
+
left: auto !important;
|
|
792
|
+
right: 0 !important;
|
|
605
793
|
background-color: var(--aa-elevated);
|
|
606
794
|
border: 1px solid var(--aa-border);
|
|
607
795
|
background-image: none;
|
|
@@ -612,6 +800,12 @@ body.active_admin {
|
|
|
612
800
|
// that is invisible whenever the panel fill is close to the page —
|
|
613
801
|
// which is exactly what a light surface is. Outline it like the panel.
|
|
614
802
|
div.dropdown_menu_nipple {
|
|
803
|
+
// The panel is pinned right (above) while AA still offsets the
|
|
804
|
+
// arrow from the panel's LEFT edge by the button's centre, which
|
|
805
|
+
// lands it on empty bar. Pin it right too, centred on the button's
|
|
806
|
+
// own caret (AA draws that 6px wide at right: 12px).
|
|
807
|
+
left: auto !important;
|
|
808
|
+
right: 9px !important;
|
|
615
809
|
border-color: var(--aa-border) transparent;
|
|
616
810
|
&:before, &:after { border-color: var(--aa-elevated) transparent; }
|
|
617
811
|
}
|
|
@@ -639,6 +833,12 @@ body.active_admin {
|
|
|
639
833
|
}
|
|
640
834
|
}
|
|
641
835
|
.flashes {
|
|
836
|
+
// Pin like #header/#title_bar: this is the one full-width bar between the
|
|
837
|
+
// two pinned ones, and on a wide page scrolled right its left-aligned text
|
|
838
|
+
// would leave the viewport, leaving an unreadable colored stripe. AA puts
|
|
839
|
+
// all padding/background on the inner .flash, so the always-rendered empty
|
|
840
|
+
// wrapper stays zero-height and inert on flash-less pages.
|
|
841
|
+
@include viewport-pinned-bar;
|
|
642
842
|
.flash {
|
|
643
843
|
color: #ffffff;
|
|
644
844
|
background-image: none;
|
|
@@ -1216,6 +1416,38 @@ table.index_table thead th a {
|
|
|
1216
1416
|
color: var(--aa-table-header-text) !important;
|
|
1217
1417
|
}
|
|
1218
1418
|
|
|
1419
|
+
// Sticky table headers: keep the column labels visible while a long index table
|
|
1420
|
+
// scrolls vertically. The header/title bar pin only horizontally (no `top`), so
|
|
1421
|
+
// they scroll away on vertical scroll and this thead pins to the viewport top —
|
|
1422
|
+
// no overlap. Scoped to .paginated_collection so it only hits real AA index
|
|
1423
|
+
// listings, not custom report/dashboard tables.
|
|
1424
|
+
//
|
|
1425
|
+
// Opting out: AA 3.x hardcodes the index table's class attribute and ignores
|
|
1426
|
+
// `index class:` options, so .skip-sticky-headers is reachable only on tables
|
|
1427
|
+
// whose markup you render yourself. For real AA listings, opt out app-wide
|
|
1428
|
+
// with one CSS line in your own stylesheet:
|
|
1429
|
+
// .paginated_collection table.index_table thead tr th { position: static; }
|
|
1430
|
+
// Opt a hand-built table IN with .with-sticky-headers.
|
|
1431
|
+
// tr:first-child only: with a multi-row thead (group header over column
|
|
1432
|
+
// header) every row would stick at the same offset and paint over each other —
|
|
1433
|
+
// per-row offsets need measurements CSS alone can't express. The first row
|
|
1434
|
+
// pins; any further header rows scroll away normally.
|
|
1435
|
+
.paginated_collection table.index_table:not(.skip-sticky-headers) thead tr:first-child th,
|
|
1436
|
+
table.with-sticky-headers thead tr:first-child th {
|
|
1437
|
+
position: sticky;
|
|
1438
|
+
top: -1px;
|
|
1439
|
+
z-index: 98;
|
|
1440
|
+
}
|
|
1441
|
+
// Square the corner cells of sticky theads: their inner-radius top corners are
|
|
1442
|
+
// transparent, so once the header detaches from the table's rounded border the
|
|
1443
|
+
// rows scrolling beneath flicker through the ~3px notches. At rest the square
|
|
1444
|
+
// background overpaints the border's rounding by the same ~3px — the lesser,
|
|
1445
|
+
// static artifact (CSS cannot tell "stuck" from "at rest").
|
|
1446
|
+
.paginated_collection table.index_table:not(.skip-sticky-headers) thead tr:first-child th:first-child,
|
|
1447
|
+
table.with-sticky-headers thead tr:first-child th:first-child { border-top-left-radius: 0; }
|
|
1448
|
+
.paginated_collection table.index_table:not(.skip-sticky-headers) thead tr:first-child th:last-child,
|
|
1449
|
+
table.with-sticky-headers thead tr:first-child th:last-child { border-top-right-radius: 0; }
|
|
1450
|
+
|
|
1219
1451
|
// Form section header: a <legend> interrupts the fieldset's top border, so the
|
|
1220
1452
|
// header bar has no border around it (unlike a panel header). Float the legend
|
|
1221
1453
|
// so it flows as a normal block and the fieldset border wraps around it.
|
|
@@ -1360,7 +1592,47 @@ form fieldset > ol > li fieldset ol li {
|
|
|
1360
1592
|
border-color: var(--aa-border);
|
|
1361
1593
|
}
|
|
1362
1594
|
.dropdown_menu .dropdown_menu_list_wrapper .dropdown_menu_list li {
|
|
1363
|
-
|
|
1595
|
+
// Was repainted to --aa-border, but a hairline under every row makes the
|
|
1596
|
+
// flyout read as a table of cells rather than a menu. Drop it — the hover
|
|
1597
|
+
// highlight is enough separation. (Same depth as the base rule, later wins.)
|
|
1598
|
+
border-bottom: none;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// Dropdown-button caret. ActiveAdmin draws it with two stacked triangles on the
|
|
1602
|
+
// button: :before is a #FFF "shadow", :after the #777 arrow — both hard-coded.
|
|
1603
|
+
// On the themed blue title-bar action button and on the dark elevated
|
|
1604
|
+
// table-tools buttons the #777 arrow is ~invisible. Recolor it to the button's
|
|
1605
|
+
// own text colour and hide the light shadow into the fill.
|
|
1606
|
+
#titlebar_right .dropdown_menu .dropdown_menu_button {
|
|
1607
|
+
&:before { border-top-color: var(--aa-button); }
|
|
1608
|
+
&:after { border-top-color: var(--aa-button-text); }
|
|
1609
|
+
}
|
|
1610
|
+
.table_tools .dropdown_menu .dropdown_menu_button,
|
|
1611
|
+
.batch_actions_selector .dropdown_menu_button {
|
|
1612
|
+
&:before { border-top-color: var(--aa-elevated); }
|
|
1613
|
+
&:after { border-top-color: var(--aa-text); }
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// Disabled form controls. The theme recolors inputs/selects via --aa-*, but a
|
|
1617
|
+
// disabled one looked identical to an enabled one (especially in dark). Mute
|
|
1618
|
+
// the fill, text and cursor so "disabled" reads in both modes. select2 sets its
|
|
1619
|
+
// control bg with !important, so the disabled override needs it too.
|
|
1620
|
+
#active_admin_content {
|
|
1621
|
+
input:disabled, textarea:disabled, select:disabled {
|
|
1622
|
+
background-color: var(--aa-surface-2) !important;
|
|
1623
|
+
color: var(--aa-text-muted) !important;
|
|
1624
|
+
opacity: 0.6;
|
|
1625
|
+
cursor: not-allowed !important;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
// Both container classes (--default --disabled) so this out-specifies the
|
|
1629
|
+
// enabled control rule's `--default` fill (which is !important) regardless of
|
|
1630
|
+
// source order.
|
|
1631
|
+
.select2-container--default.select2-container--disabled .select2-selection {
|
|
1632
|
+
background-color: var(--aa-surface-2) !important;
|
|
1633
|
+
opacity: 0.6;
|
|
1634
|
+
cursor: not-allowed !important;
|
|
1635
|
+
.select2-selection__rendered { color: var(--aa-text-muted) !important; }
|
|
1364
1636
|
}
|
|
1365
1637
|
|
|
1366
1638
|
.ui-dialog {
|
|
@@ -1370,6 +1642,11 @@ form fieldset > ol > li fieldset ol li {
|
|
|
1370
1642
|
background-color: var(--aa-surface);
|
|
1371
1643
|
color: var(--aa-text);
|
|
1372
1644
|
border-bottom: 1px solid var(--aa-border);
|
|
1645
|
+
// jQuery-UI's default `minHeight: 150` option is applied inline to the content
|
|
1646
|
+
// box (min-height:~54px), so a one-line confirm leaves a big empty area
|
|
1647
|
+
// between the message and the buttons. Let the content size to its text
|
|
1648
|
+
// (the inline min-height needs !important to beat).
|
|
1649
|
+
.ui-dialog-content { min-height: 0 !important; }
|
|
1373
1650
|
.ui-dialog-titlebar {
|
|
1374
1651
|
background-image: none;
|
|
1375
1652
|
box-shadow: none;
|
|
@@ -1465,6 +1742,28 @@ form.filter_form .filter_form_field.filter_date_range .seperator {
|
|
|
1465
1742
|
width: 10px;
|
|
1466
1743
|
}
|
|
1467
1744
|
|
|
1745
|
+
// Date-only range filters (jQuery-UI datepicker, input.datepicker) get no width
|
|
1746
|
+
// from the base, so the two inputs fall back to their size="12" intrinsic width
|
|
1747
|
+
// and line up side by side only by luck; the label's float:right "Set range"
|
|
1748
|
+
// link then leaks into the input band on a longer label and bumps the "To"
|
|
1749
|
+
// input onto a second line. Lay the field out with flexbox: the label takes a
|
|
1750
|
+
// full row, the two inputs share the next, immune to the float.
|
|
1751
|
+
form.filter_form .filter_form_field.filter_date_range {
|
|
1752
|
+
display: flex;
|
|
1753
|
+
flex-wrap: wrap;
|
|
1754
|
+
|
|
1755
|
+
> label { flex: 0 0 100%; }
|
|
1756
|
+
|
|
1757
|
+
input.datepicker {
|
|
1758
|
+
box-sizing: border-box;
|
|
1759
|
+
flex: 1 1 0;
|
|
1760
|
+
min-width: 0;
|
|
1761
|
+
margin: 0;
|
|
1762
|
+
|
|
1763
|
+
& + input.datepicker { margin-left: 6px; }
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1468
1767
|
//filter select box border radius
|
|
1469
1768
|
.filter_form_field select {
|
|
1470
1769
|
border-radius: 3px;
|
|
@@ -1549,8 +1848,10 @@ input[type='radio'] {
|
|
|
1549
1848
|
// the seven :not()s are worth seven class-level points, and that beat every
|
|
1550
1849
|
// component rule declared later — pagination links and the Clear Filters
|
|
1551
1850
|
// button came out --aa-link instead of --aa-text.
|
|
1552
|
-
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); }
|
|
1553
|
-
|
|
1851
|
+
a:where(:not(.button):not(.action_item):not(.table_tools_button):not(.dropdown_menu_button):not(.ui-tabs-anchor):not(.delete_link):not(.delete-link):not([data-method="delete"])) { color: var(--aa-link); }
|
|
1852
|
+
// `.delete-link` (hyphen) as well as AA's own `.delete_link`: some apps name
|
|
1853
|
+
// their soft-delete links with the hyphen.
|
|
1854
|
+
a[data-method="delete"], a.delete_link, a.delete-link { color: var(--aa-delete-link); }
|
|
1554
1855
|
|
|
1555
1856
|
// Pagination — non-current page chips use a light-grey bg in the theme
|
|
1556
1857
|
.pagination span > a { background-color: var(--aa-surface-2); color: var(--aa-text); }
|
|
@@ -1560,7 +1861,10 @@ input[type='radio'] {
|
|
|
1560
1861
|
// cannot be out-specified by anything sane here, which is why this needs
|
|
1561
1862
|
// !important, like the accent fill one line over in the base pagination
|
|
1562
1863
|
// block. Without it the digit renders at 1.00:1 on its own background.
|
|
1563
|
-
|
|
1864
|
+
// The hover fill is the accent ($skinMainSecondColor); $skinMenuPillTextColor
|
|
1865
|
+
// is a light blue that sits on it at ~1.16:1 (invisible). White reads on the
|
|
1866
|
+
// accent (matches the base pagination block's own hover colour one file over).
|
|
1867
|
+
.pagination span > a:hover { color: #fff !important; }
|
|
1564
1868
|
|
|
1565
1869
|
// Batch-actions / table-tools dropdown menu items are white by default
|
|
1566
1870
|
.table_tools .dropdown_menu_list li a {
|
|
@@ -1632,12 +1936,25 @@ input[type='radio'] {
|
|
|
1632
1936
|
table.index_table tbody tr:nth-child(even) > td,
|
|
1633
1937
|
table.index_table tbody tr.even > td { background: var(--aa-surface-2); }
|
|
1634
1938
|
table.index_table tbody tr:hover > td { background: var(--aa-surface-hover); }
|
|
1635
|
-
//
|
|
1636
|
-
// `tr.selected td`, so without this a ticked row
|
|
1637
|
-
//
|
|
1638
|
-
//
|
|
1939
|
+
// Selected cells: the zebra and hover rules above outrank ActiveAdmin's own
|
|
1940
|
+
// `tr.selected td` / `td.selected`, so without this a ticked row (batch
|
|
1941
|
+
// actions) or a click-highlighted row + column (crisscross) looks unselected.
|
|
1942
|
+
// The `tr >` in the td.selected selector is load-bearing: the even-stripe
|
|
1943
|
+
// rule is `...tbody tr:nth-child(even) > td` (specificity 0,0,2,4); a plain
|
|
1944
|
+
// `tbody td.selected` is only (0,0,2,3) and loses on even rows, breaking the
|
|
1945
|
+
// cross every other row. Matching the `tr >` ties the specificity, so source
|
|
1946
|
+
// order wins. The :hover variants keep the selection visible under the pointer.
|
|
1639
1947
|
table.index_table tbody tr.selected > td,
|
|
1640
|
-
table.index_table tbody tr.selected:hover > td
|
|
1948
|
+
table.index_table tbody tr.selected:hover > td,
|
|
1949
|
+
table.index_table tbody tr > td.selected,
|
|
1950
|
+
table.index_table tbody tr:hover > td.selected { background: var(--aa-selected); }
|
|
1951
|
+
// The clicked cell sits at the centre of the cross; an accent outline marks
|
|
1952
|
+
// it apart from the row/column tint without a competing fill (outset inward
|
|
1953
|
+
// so it adds no layout shift).
|
|
1954
|
+
table.index_table tbody tr > td.selected-cell {
|
|
1955
|
+
outline: 2px solid var(--aa-accent);
|
|
1956
|
+
outline-offset: -2px;
|
|
1957
|
+
}
|
|
1641
1958
|
// Plain content tables (e.g. the CDR show "Attempts" table) are striped by the
|
|
1642
1959
|
// base CSS with light fills; map the odd/even rows onto the dark surfaces too.
|
|
1643
1960
|
table tbody tr.odd > td, table tbody tr.odd > th { background-color: var(--aa-surface); }
|
|
@@ -1654,11 +1971,23 @@ input[type='radio'] {
|
|
|
1654
1971
|
color: var(--aa-text);
|
|
1655
1972
|
}
|
|
1656
1973
|
|
|
1974
|
+
// Give the content column its own block-formatting context so #index_footer's
|
|
1975
|
+
// `clear: both` (below) only clears the pagination floats INSIDE the table
|
|
1976
|
+
// area — not the floated #sidebar. Without this, a tall filter sidebar pushes
|
|
1977
|
+
// the pagination far below a short table (it clears the sidebar's float too).
|
|
1978
|
+
#main_content { display: flow-root; }
|
|
1979
|
+
|
|
1657
1980
|
// Index footer (per-page selector, pagination, "displaying…" info, download
|
|
1658
1981
|
// links): lay them out in one left-packed, vertically-centred row. The gem
|
|
1659
1982
|
// floats them, so they sat top-aligned and, having different heights, looked
|
|
1660
1983
|
// misaligned. A slightly larger font than the gem default (0.85em) too.
|
|
1661
1984
|
#index_footer {
|
|
1985
|
+
// ActiveAdmin floats .pagination_top and .paginated_collection_contents
|
|
1986
|
+
// left; the footer has no clear, so it rode up beside them and rendered
|
|
1987
|
+
// ABOVE the table (per-page + page numbers + "Displaying…" duplicated on
|
|
1988
|
+
// top, nothing below). Clear the floats so it sits under the table — one
|
|
1989
|
+
// pagination on top, one on the bottom, like master.
|
|
1990
|
+
clear: both;
|
|
1662
1991
|
text-align: left;
|
|
1663
1992
|
font-size: 0.95em;
|
|
1664
1993
|
.pagination_per_page,
|
|
@@ -1711,6 +2040,83 @@ input[type='radio'] {
|
|
|
1711
2040
|
table.ui-datepicker-calendar tbody td span { color: var(--aa-text-muted); }
|
|
1712
2041
|
.ui-state-hover, .ui-state-active { background: var(--aa-surface-hover); }
|
|
1713
2042
|
}
|
|
2043
|
+
|
|
2044
|
+
// select2 (used by most ActiveAdmin apps). It ships its own light chrome and
|
|
2045
|
+
// appends the dropdown to <body>, so it is styled here at the mixin level
|
|
2046
|
+
// like the datepicker. Drive the control, the dropdown panel, the results
|
|
2047
|
+
// list and the search field from the --aa-* tokens so it follows both modes;
|
|
2048
|
+
// without this the dropdown stays a white panel with white/near-invisible
|
|
2049
|
+
// text in dark mode. !important is required because select2's own stylesheet
|
|
2050
|
+
// sets these to white with equal specificity and loads after this in the
|
|
2051
|
+
// bundle — same reason the datetime picker overrides use it.
|
|
2052
|
+
.select2-container--default {
|
|
2053
|
+
.select2-selection--single,
|
|
2054
|
+
.select2-selection--multiple {
|
|
2055
|
+
background-color: var(--aa-input-bg) !important;
|
|
2056
|
+
border-color: var(--aa-input-border) !important;
|
|
2057
|
+
color: var(--aa-text);
|
|
2058
|
+
}
|
|
2059
|
+
.select2-selection__rendered { color: var(--aa-text) !important; }
|
|
2060
|
+
.select2-selection__placeholder { color: var(--aa-text-muted) !important; }
|
|
2061
|
+
// Clear "×": keep it a small, muted inline glyph that brightens on hover.
|
|
2062
|
+
// select2 4.1 renders it as a <button> that takes a large square hover fill
|
|
2063
|
+
// (the "big blue square"); neutralize that and the 4.0 span the same way so
|
|
2064
|
+
// the affordance is just the colour change, correctly aligned.
|
|
2065
|
+
.select2-selection__clear {
|
|
2066
|
+
color: var(--aa-text-muted) !important;
|
|
2067
|
+
background: transparent !important;
|
|
2068
|
+
border: none !important;
|
|
2069
|
+
box-shadow: none !important;
|
|
2070
|
+
padding: 0 4px !important;
|
|
2071
|
+
&:hover, &:focus { color: var(--aa-text) !important; background: transparent !important; }
|
|
2072
|
+
}
|
|
2073
|
+
.select2-selection--multiple .select2-selection__choice {
|
|
2074
|
+
background-color: var(--aa-surface-2) !important;
|
|
2075
|
+
border-color: var(--aa-border) !important;
|
|
2076
|
+
color: var(--aa-text) !important;
|
|
2077
|
+
}
|
|
2078
|
+
.select2-search__field { color: var(--aa-text) !important; }
|
|
2079
|
+
}
|
|
2080
|
+
.select2-dropdown {
|
|
2081
|
+
background-color: var(--aa-surface) !important;
|
|
2082
|
+
border-color: var(--aa-border) !important;
|
|
2083
|
+
color: var(--aa-text);
|
|
2084
|
+
}
|
|
2085
|
+
.select2-results__option { color: var(--aa-text) !important; }
|
|
2086
|
+
.select2-results__option--highlighted[aria-selected] {
|
|
2087
|
+
background-color: var(--aa-surface-hover) !important;
|
|
2088
|
+
color: var(--aa-text) !important;
|
|
2089
|
+
}
|
|
2090
|
+
.select2-results__option[aria-selected="true"] {
|
|
2091
|
+
background-color: var(--aa-selected) !important;
|
|
2092
|
+
color: var(--aa-text) !important;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
// tom-select (used by e.g. yeti). Same situation as select2: its own light
|
|
2096
|
+
// chrome, dropdown rendered in place; recolor from the --aa-* tokens.
|
|
2097
|
+
.ts-wrapper {
|
|
2098
|
+
.ts-control {
|
|
2099
|
+
background: var(--aa-input-bg) !important;
|
|
2100
|
+
border-color: var(--aa-input-border) !important;
|
|
2101
|
+
color: var(--aa-text);
|
|
2102
|
+
input, input::placeholder { color: var(--aa-text) !important; }
|
|
2103
|
+
.item {
|
|
2104
|
+
background: var(--aa-surface-2) !important;
|
|
2105
|
+
border-color: var(--aa-border) !important;
|
|
2106
|
+
color: var(--aa-text) !important;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
.ts-dropdown {
|
|
2111
|
+
background: var(--aa-surface) !important;
|
|
2112
|
+
border-color: var(--aa-border) !important;
|
|
2113
|
+
color: var(--aa-text);
|
|
2114
|
+
.optgroup-header { background: var(--aa-surface-2) !important; color: var(--aa-text-muted) !important; }
|
|
2115
|
+
.option { color: var(--aa-text) !important; }
|
|
2116
|
+
.option.active,
|
|
2117
|
+
.active { background: var(--aa-surface-hover) !important; color: var(--aa-text) !important; }
|
|
2118
|
+
.option[aria-selected="true"] { background: var(--aa-selected) !important; color: var(--aa-text) !important; }
|
|
2119
|
+
}
|
|
1714
2120
|
}
|
|
1715
2121
|
|
|
1716
2122
|
// DARK-ONLY content overrides — genuinely dark-specific values with no light
|
|
@@ -1729,3 +2135,58 @@ input[type='radio'] {
|
|
|
1729
2135
|
html:not([data-theme="light"]) { @include aa-dark-content; }
|
|
1730
2136
|
}
|
|
1731
2137
|
html[data-theme="dark"] { @include aa-dark-content; }
|
|
2138
|
+
|
|
2139
|
+
// Trix / ActionText editor. Trix ships with ActionText (a Rails built-in), so
|
|
2140
|
+
// its chrome is themed here for every consumer; the app still imports Trix's
|
|
2141
|
+
// own base CSS (trix/dist/trix). All rules key off the --aa-* tokens, so they
|
|
2142
|
+
// follow the active palette in both light and dark, and are inert when no
|
|
2143
|
+
// editor is on the page.
|
|
2144
|
+
trix-toolbar {
|
|
2145
|
+
.trix-button-group { border-color: var(--aa-border); }
|
|
2146
|
+
.trix-button {
|
|
2147
|
+
background: var(--aa-surface);
|
|
2148
|
+
color: var(--aa-text);
|
|
2149
|
+
border-bottom-color: var(--aa-border);
|
|
2150
|
+
&:not(:first-child) { border-left-color: var(--aa-border); }
|
|
2151
|
+
&:not(:disabled):hover { background: var(--aa-surface-hover); }
|
|
2152
|
+
&.trix-active { background: var(--aa-selected); }
|
|
2153
|
+
}
|
|
2154
|
+
// Trix dims idle icons to opacity 0.6 and disabled ones to 0.125 — both read
|
|
2155
|
+
// as faint in either palette, the 0.125 so faint the block-format buttons
|
|
2156
|
+
// (disabled until the editor is focused) look broken. Lift idle to 0.8 and
|
|
2157
|
+
// disabled to 0.4: still clearly dimmer than idle, but legible. Trix keeps the
|
|
2158
|
+
// active icon at 1 via a higher-specificity rule.
|
|
2159
|
+
.trix-button--icon::before { opacity: 0.8; }
|
|
2160
|
+
.trix-button--icon:disabled::before { opacity: 0.4; }
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
trix-editor {
|
|
2164
|
+
background: var(--aa-input-bg);
|
|
2165
|
+
border-color: var(--aa-input-border);
|
|
2166
|
+
color: var(--aa-text);
|
|
2167
|
+
&:empty:not(:focus)::before { color: var(--aa-text-muted); } // placeholder
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
.trix-content {
|
|
2171
|
+
pre { background: var(--aa-surface-2); }
|
|
2172
|
+
blockquote { border-left: 0.3em solid var(--aa-border); }
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
.trix-dialog {
|
|
2176
|
+
background: var(--aa-surface);
|
|
2177
|
+
color: var(--aa-text);
|
|
2178
|
+
box-shadow: 0 2px 8px var(--aa-shadow);
|
|
2179
|
+
input[type="text"], input[type="url"] {
|
|
2180
|
+
background: var(--aa-input-bg);
|
|
2181
|
+
border-color: var(--aa-input-border);
|
|
2182
|
+
color: var(--aa-text);
|
|
2183
|
+
}
|
|
2184
|
+
.trix-button { background: var(--aa-accent); color: #fff; }
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
// Trix bakes its toolbar icons into the base CSS as black SVG background images
|
|
2188
|
+
// on ::before; invert them in dark mode so they read on the dark toolbar (the
|
|
2189
|
+
// idle/disabled opacity lift above applies in both modes).
|
|
2190
|
+
@include aa-dark {
|
|
2191
|
+
trix-toolbar .trix-button--icon::before { filter: invert(1); }
|
|
2192
|
+
}
|