diff --git a/app/assets/stylesheets/wigu/active_admin_theme.scss b/app/assets/stylesheets/wigu/active_admin_theme.scss index 9f783a9..76b3c35 100644 --- a/app/assets/stylesheets/wigu/active_admin_theme.scss +++ b/app/assets/stylesheets/wigu/active_admin_theme.scss @@ -6,13 +6,50 @@ $skinBorderRadius: 4px!default; $skinBorderWindowColor: #e6e9ee!default; $skinTablePadding: 10px!default; +// Header menu colors. Defaults follow $skinMainSecondColor, so a project that +// overrides it still gets the menu following that accent. Every colour here has +// a matching text colour, otherwise a light value would be unreadable. +$skinMenuPillColor: $skinMainSecondColor!default; // top-level current/hover pill +$skinMenuTextColor: #ffffff!default; // dropdown item text, hover/current included +// Follows the dropdown text rather than defaulting to white on its own: a +// project that light-themes the menu sets one variable, not two. +$skinMenuPillTextColor: $skinMenuTextColor!default; // text on that pill +$skinMenuPanelColor: $skinMainSecondColor!default; // dropdown panel bg + hover "bridge" border +$skinMenuItemHoverColor: transparent!default; // dropdown item hover/current bg + +// Header / menu sizing. NOTE: not a no-op refactor — dropdown item height and +// dropdown width change for every project, configured or not. Header padding +// keeps its previous asymmetric values, so the header itself does not move. +$skinMenuFontSize: 1em!default; // top-level tab text size (does not cascade into dropdowns) +$skinMenuItemPaddingY: 8px!default; // dropdown item top/bottom padding (was 6px/4px + a 7px border) +$skinMenuItemLineHeight: 1.5!default; // dropdown item line-height +$skinMenuPanelMaxWidth: 260px!default; // dropdown panel ceiling; longer labels wrap instead of leaving the viewport +$skinHeaderPaddingTop: 5px!default; // header top padding (base value, kept so the header does not shift) +$skinHeaderPaddingBottom: 9px!default; // header bottom padding +$skinHeaderLogoMaxHeight: none!default; // cap the site_title logo image height + +// Title bar +$skinTitleBarColor: lighten($skinMainFirstColor, 8%)!default; +$skinTitleBarBorderColor: $skinMainSecondColor!default; +$skinTitleBarBorderWidth: 3px!default; +$skinTitleBarButtonPaddingY: 10px!default; // action button vertical padding +$skinTitleBarButtonPaddingX: 20px!default; // action button horizontal padding + // A wrong-typed override is legal SassScript, so it compiles without a word and // emits CSS the browser drops — `border-bottom: none solid #5ea3d3`, // `padding: 10 12px`. The rule silently vanishes and ActiveAdmin's own value // reappears. Fail the build with a readable message instead. @each $name, $value in ( skinBorderRadius: $skinBorderRadius, - skinTablePadding: $skinTablePadding + skinTablePadding: $skinTablePadding, + skinMenuFontSize: $skinMenuFontSize, + skinMenuItemPaddingY: $skinMenuItemPaddingY, + skinMenuPanelMaxWidth: $skinMenuPanelMaxWidth, + skinHeaderPaddingTop: $skinHeaderPaddingTop, + skinHeaderPaddingBottom: $skinHeaderPaddingBottom, + skinTitleBarBorderWidth: $skinTitleBarBorderWidth, + skinTitleBarButtonPaddingY: $skinTitleBarButtonPaddingY, + skinTitleBarButtonPaddingX: $skinTitleBarButtonPaddingX ) { @if type-of($value) != number or (unitless($value) and $value != 0) { @error "$#{$name} must be a length with a unit (e.g. 10px), got `#{$value}`."; @@ -21,7 +58,14 @@ $skinTablePadding: 10px!default; @each $name, $value in ( skinMainFirstColor: $skinMainFirstColor, skinMainSecondColor: $skinMainSecondColor, - skinBorderWindowColor: $skinBorderWindowColor + skinBorderWindowColor: $skinBorderWindowColor, + skinMenuPillColor: $skinMenuPillColor, + skinMenuPillTextColor: $skinMenuPillTextColor, + skinMenuPanelColor: $skinMenuPanelColor, + skinMenuItemHoverColor: $skinMenuItemHoverColor, + skinMenuTextColor: $skinMenuTextColor, + skinTitleBarColor: $skinTitleBarColor, + skinTitleBarBorderColor: $skinTitleBarBorderColor ) { @if type-of($value) != color { @error "$#{$name} must be a color (use `transparent`, not `none`), got `#{$value}`."; @@ -80,52 +124,87 @@ body.active_admin { #header { background-color: $skinMainFirstColor; border-bottom: 1px solid lighten( $skinMainFirstColor, 15% )!important; - padding-bottom: 9px; + padding-top: $skinHeaderPaddingTop; + padding-bottom: $skinHeaderPaddingBottom; border: none; text-shadow: none; background-image: none; h1.site_title { color: $skinMainSecondColor; font-size: 16px; + img { + max-height: $skinHeaderLogoMaxHeight; + } } a, a:link { color: #ffffff; } ul.tabs > li > a { - color: #ffffff; + // On the anchor, not the li, so the dropdown subtree does not inherit it. + font-size: $skinMenuFontSize; @include rounded($skinBorderRadius); padding: 6px 10px 6px 10px; } ul.tabs > li.current > a, ul.tabs > li:hover > a { - background-color: $skinMainSecondColor; + background-color: $skinMenuPillColor; + color: $skinMenuPillTextColor; } ul.tabs { li.has_nested { - a { + // `> a` only: leaf items need no room for the submenu arrow. + > a { padding-right: 20px; } ul { - background-color: $skinMainSecondColor; - @include rounded($skinBorderRadius); + background-color: $skinMenuPanelColor; + @include rounded-all(0, $skinBorderRadius, $skinBorderRadius, $skinBorderRadius); + // Size to content instead of the base's max-width: calc(100% + 20px), + // which wraps long labels onto a second line — but keep a ceiling, or + // a long label pushes the panel out of the viewport where the :hover + // chain cannot follow it. Past the ceiling, labels wrap as before. + // Known limit: a depth-2 flyout under a right-edge tab can still leave + // a 1280px viewport — it sits at left:100% of an already-offset panel, + // and CSS cannot see the distance to the viewport edge. ActiveAdmin's + // own 175px panels already land within 5px of the edge there, so any + // widening inherits this. A full fix needs JS that flips the flyout to + // `right:100%`, or CSS anchor positioning once it is baseline. + width: max-content; + max-width: $skinMenuPanelMaxWidth; + overflow-wrap: break-word; li > a { padding-left: 15px; + color: $skinMenuTextColor; + line-height: $skinMenuItemLineHeight; + padding-top: $skinMenuItemPaddingY; + padding-bottom: $skinMenuItemPaddingY; } - li.current > a::before, li:hover > a::before { + li:hover > a, li.current > a, li > a:focus { + background-color: $skinMenuItemHoverColor; + } + li.current > a::before, li:hover > a::before, li > a:focus::before { content: ''; display: block; - width: 7px; - height: 4px; position: absolute; - top: 12px; + top: 50%; left: 4px; - background: transparent url($menu-arrow-dark-icon-url) no-repeat 0 0; - transform: rotate(-90deg); + // currentColor, so the marker follows $skinMenuTextColor instead of + // a fixed-palette PNG that only reads against the default panel. + border: 3px solid transparent; + border-left-color: currentColor; + margin-top: -3px; } } } - li.has_nested:hover { - a { - border-bottom: 7px solid $skinMainSecondColor; + // `> li.has_nested` + `> a` so the "bridge" border is added only to a + // hovered TOP-LEVEL menu item (bridging its pill to the dropdown). Without + // the `>`, nested has_nested items (e.g. System > Components) also got the + // border, which pushed the next dropdown item down when their submenu + // opened. 5px to match the base's `ul { margin-top: 5px }` gap, and square + // off the corners that face the panel so the header does not show through. + > li.has_nested:hover { + > a { + @include rounded-top($skinBorderRadius); + border-bottom: 5px solid $skinMenuPanelColor; } } } @@ -134,9 +213,9 @@ body.active_admin { background-image: none; border: none; box-shadow: none; - background-color: lighten( $skinMainFirstColor, 8% ); + background-color: $skinTitleBarColor; border: none; - border-bottom: 3px solid $skinMainSecondColor; + border-bottom: $skinTitleBarBorderWidth solid $skinTitleBarBorderColor; #titlebar_left { span.breadcrumb { font-size: 12px; @@ -160,7 +239,7 @@ body.active_admin { @include rounded($skinBorderRadius); background-color: $skinMainSecondColor; border: none; - padding: 10px 20px; + padding: $skinTitleBarButtonPaddingY $skinTitleBarButtonPaddingX; color: #ffffff; font-weight: 600; &:hover { @@ -193,21 +272,6 @@ body.active_admin { } } } - div.batch_actions_selector { - a { - background-image: none; - border: none; - box-shadow: none; - background-color: $skinMainSecondColor; - @include rounded($skinBorderRadius); - border: none; - padding: 10px 20px; - color: #ffffff; - &:hover { - background-color: lighten( $skinMainSecondColor, 5% ); - } - } - } } } .flashes { diff --git a/test/css_check.rb b/test/css_check.rb index 76968d9..8d86494 100644 --- a/test/css_check.rb +++ b/test/css_check.rb @@ -18,6 +18,10 @@ module CssCheck "dark chrome" => '$skinMainFirstColor: #111418; $skinBorderWindowColor: #b8babe;', "square + wide" => '$skinBorderRadius: 0; $skinTablePadding: 14px;', "rem radius" => '$skinBorderRadius: 0.25rem;', + "light menu" => '$skinMenuPanelColor: #ffffff; $skinMenuTextColor: #333333; + $skinMenuPillColor: #f0f0f0; $skinMenuPillTextColor: #222222;', + "dark menu" => '$skinMenuPanelColor: #222222; $skinMenuItemHoverColor: #3a3a3a;', + "roomy menu" => '$skinMenuFontSize: 1.6em; $skinMenuItemPaddingY: 12px;', }.freeze # Wrong-typed overrides. All of these are legal SassScript, so without the @@ -25,9 +29,12 @@ module CssCheck # drops — the rule simply vanishes and ActiveAdmin's own value reappears, with # nothing in the build output to say why. BAD = { - "$skinBorderRadius without a unit" => '$skinBorderRadius: 4;', - "$skinTablePadding: none" => '$skinTablePadding: none;', - "$skinBorderWindowColor: none" => '$skinBorderWindowColor: none;', + "$skinBorderRadius without a unit" => '$skinBorderRadius: 4;', + "$skinTablePadding: none" => '$skinTablePadding: none;', + "$skinBorderWindowColor: none" => '$skinBorderWindowColor: none;', + "$skinMenuItemPaddingY without a unit" => '$skinMenuItemPaddingY: 8;', + "$skinMenuItemHoverColor: none" => '$skinMenuItemHoverColor: none;', + "$skinTitleBarBorderWidth: none" => '$skinTitleBarBorderWidth: none;', }.freeze def self.load_paths @@ -61,6 +68,16 @@ def self.run # expected — the theme's type guards caught it end + # The header menu's text colours must follow the variables. A hard-coded + # white that out-specifies $skinMenuTextColor is invisible on a light panel, + # which is the whole point of being able to set the panel colour. + menu = compile(GOOD["light menu"]).scan(/^[^{}]*#wrapper #header ul\.tabs[^{}]*\{[^}]*\}/m).join("\n") + whites = menu.scan(/^\s*color:\s*(?:#f{3,6}|white)\s*;/i) + unless whites.empty? + failures << "light menu: #{whites.size} hard-coded white colour(s) left in the header menu; " \ + "they out-specify $skinMenuTextColor and render invisible on a light panel" + end + if failures.empty? puts "css_check: #{GOOD.size} overrides compile clean, #{BAD.size} bad ones rejected" else