mirror of
https://github.com/cockpit-project/cockpit.git
synced 2026-11-03 21:49:50 -09:00
In the mobile layout the "System" label sits 4px lower than the "Help" and "Session" labels and its box overflows the navbar. The nav toggle rule also matched `#nav-system-item > .pf-v6-c-button__text`, so the label span got its own `display: grid`, `grid-template-rows: 28px 1fr` and `block-size: 100%`. It renders inside a 28px row of its own, which pushes the text down. The button itself already matches `button.ct-nav-toggle`, and the nested rules in that block target menu-toggle/select children that the label span never has. Dropping the selector puts the label at the same height as the other navbar labels. Follow-up to #23083. Signed-off-by: Peter Buchan Jr <pbuchan@redhat.com> Assisted-by: Claude Opus 5.5
659 lines
16 KiB
SCSS
659 lines
16 KiB
SCSS
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
@use "global-variables" as *;
|
|
|
|
/* Navigation base, used for both desktop & mobile navigation */
|
|
|
|
$phone-tiny: 360px;
|
|
$phone: 767px;
|
|
$desktop: $phone + 1px;
|
|
|
|
:root {
|
|
--ct-color-nav-menu: var(--pf-t--global--background--color--secondary--default);
|
|
--ct-color-nav-highlight: var(--pf-t--global--background--color--highlight--default);
|
|
--ct-color-nav-highlight-text: var(--pf-t--global--text--color--on-highlight);
|
|
--ct-color-nav-search: #1b1b1b;
|
|
}
|
|
|
|
.area-ct-subnav {
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
|
|
// Overrides for dark theme (as PF's dark theme is currently odd with nav)
|
|
.pf-v6-theme-dark & {
|
|
.pf-m-link {
|
|
color: var(--pf-t--global--text--color--link--default);
|
|
}
|
|
}
|
|
|
|
mark {
|
|
font-weight: var(--pf-t--global--font--weight--body--bold);
|
|
background: var(--ct-color-nav-highlight);
|
|
color: var(--ct-color-nav-highlight-text);
|
|
}
|
|
|
|
.nav-item-hint {
|
|
display: inline-block;
|
|
font-size: var(--pf-t--global--font--size--body--sm);
|
|
font-weight: var(--pf-t--global--font--weight--100);
|
|
inline-size: 100%;
|
|
}
|
|
|
|
// Add hover effect for clear search link
|
|
.non-menu-item .pf-m-link {
|
|
color: var(--pf-t--global--text--color--link--default);
|
|
}
|
|
|
|
.non-menu-item:hover .pf-m-link {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.non-menu-item {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.no-results {
|
|
padding-block: var(--pf-t--global--spacer--md);
|
|
padding-inline: 0;
|
|
}
|
|
}
|
|
|
|
.area-ct-content {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: block;
|
|
|
|
> iframe {
|
|
block-size: 100%;
|
|
position: absolute;
|
|
}
|
|
}
|
|
|
|
/* Desktop navigation */
|
|
@media (min-width: $desktop) {
|
|
// Adapt PF4's new header select for 2-up
|
|
.ct-topnav-content .pf-v6-c-toolbar__item:not(.super-user-indicator) {
|
|
// Remove the margin spacing between each
|
|
margin-inline-end: 0;
|
|
|
|
// Collapse the 2 side border lines into 1
|
|
+ .pf-v6-c-toolbar__item button::before {
|
|
margin-inline-start: -1px;
|
|
}
|
|
}
|
|
|
|
.host-apps {
|
|
/* FIXME: We need to change the widgets to PF6 ones; instead of CSS to fake it */
|
|
margin-inline-start: var(--pf-t--global--spacer--inset--page-chrome);
|
|
}
|
|
|
|
.mobile_v {
|
|
display: none !important;
|
|
}
|
|
|
|
.view-hosts {
|
|
background: transparent;
|
|
}
|
|
|
|
.view-hosts .sidebar-hosts {
|
|
animation: navHostsSlide 300ms ease-out;
|
|
transform-origin: top;
|
|
inset: 0;
|
|
background: var(--pf-t--global--background--color--primary--default);
|
|
/* Modals have 400 and modals should be in front of host switcher */
|
|
z-index: 399;
|
|
box-shadow: var(--pf-t--global--box-shadow--md);
|
|
block-size: fit-content;
|
|
padding: var(--pf-t--global--spacer--gutter--default);
|
|
position: relative;
|
|
border-radius: var(--pf-t--global--border--radius--medium);
|
|
|
|
&.edit-hosts {
|
|
.pf-v6-c-nav__list {
|
|
.pf-v6-c-button {
|
|
animation: navHostsEditButtonsAppear 400ms;
|
|
animation-timing-function: ease-in-out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Move host switcher away from the host line and page edges */
|
|
.navbar {
|
|
margin: var(--pf-t--global--spacer--xs);
|
|
}
|
|
|
|
.header {
|
|
.credential-unlocked {
|
|
/* AAA contrast: simulate #a1a1a1 on #151515 */
|
|
opacity: 0.6;
|
|
}
|
|
}
|
|
|
|
.ct-switcher {
|
|
.pf-v6-c-select__toggle-wrapper {
|
|
display: flex;
|
|
}
|
|
|
|
> .pf-v6-c-select > button {
|
|
padding-inline-start: var(--pf-t--global--spacer--lg);
|
|
display: flex;
|
|
align-items: center;
|
|
flex-direction: row;
|
|
flex-wrap: nowrap;
|
|
justify-content: space-between;
|
|
inline-size: 100%;
|
|
}
|
|
}
|
|
|
|
.nav-hosts-menu {
|
|
grid-area: switcher;
|
|
position: absolute;
|
|
/* Inset for the top */
|
|
inset-block: var(--pf-t--global--spacer--3xl) auto;
|
|
inset-inline: var(--pf-t--global--spacer--sm) auto;
|
|
|
|
&:not(.interact) {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.pf-v6-c-select__toggle-arrow {
|
|
font-size: 1.3rem;
|
|
transition-duration: 0.3s;
|
|
}
|
|
|
|
.clicked {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.ct-nav-toggle:not(.pf-v6-c-menu) {
|
|
border-radius: var(--pf-t--global--border--radius--small);
|
|
background: transparent;
|
|
|
|
&:hover {
|
|
background: var(--pf-t--global--background--color--action--plain--alt--hover);
|
|
}
|
|
|
|
&:hover, &:active, &.active, &.interact, &[aria-expanded="true"] {
|
|
text-decoration: none;
|
|
|
|
.hostname {
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
|
|
&:focus {
|
|
text-decoration: none;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Mobile navigation */
|
|
@media (max-width: $phone) {
|
|
:root {
|
|
--nav-icon-size: 4rem;
|
|
}
|
|
|
|
.desktop_v {
|
|
display: none !important;
|
|
}
|
|
|
|
.area-ct-subnav {
|
|
max-block-size: calc(100vh - var(--nav-icon-size));
|
|
align-self: flex-end;
|
|
z-index: 2;
|
|
|
|
&:not(.interact) {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.area-ct-subnav {
|
|
transform-origin: bottom;
|
|
animation: navHostsSlide 200ms ease-out;
|
|
}
|
|
|
|
.header {
|
|
.pf-v6-c-toolbar__item {
|
|
margin-inline-end: 0;
|
|
}
|
|
}
|
|
|
|
.nav-system-menu {
|
|
inset-inline: 0;
|
|
block-size: auto;
|
|
overflow: auto;
|
|
max-inline-size: 29rem;
|
|
display: block;
|
|
}
|
|
|
|
.nav-hosts-menu {
|
|
position: absolute;
|
|
inset-block-end: var(--nav-icon-size);
|
|
inset-inline-end: var(--pf-t--global--spacer--xs);
|
|
max-inline-size: 100vw;
|
|
}
|
|
|
|
.nav-system-menu,
|
|
.view-hosts {
|
|
background: var(--pf-t--global--background--color--primary--default);
|
|
border-radius: var(--pf-t--global--border--radius--small);
|
|
box-shadow: var(--pf-t--global--box-shadow--md);
|
|
}
|
|
|
|
.pf-m-dark.pf-v6-c-select {
|
|
.pf-v6-c-select__toggle {
|
|
background-color: transparent;
|
|
}
|
|
}
|
|
|
|
.ct-switcher > .pf-v6-c-select {
|
|
.pf-v6-c-select__toggle {
|
|
display: block;
|
|
inline-size: auto;
|
|
}
|
|
|
|
.pf-v6-c-select__toggle-arrow {
|
|
transform: scale(1.5);
|
|
margin: unset;
|
|
margin-block-start: 4px;
|
|
transition-duration: 0.3s;
|
|
}
|
|
|
|
.clicked {
|
|
transform : rotate(180deg) scale(1.5);
|
|
}
|
|
|
|
> button {
|
|
padding-inline-start: 0;
|
|
}
|
|
}
|
|
|
|
.menu-toggle,
|
|
.sidebar-toggle .fa {
|
|
display: block;
|
|
transform: scale(1.5);
|
|
border-radius: 0 !important;
|
|
}
|
|
|
|
.navbar-pf {
|
|
.ct-switcher {
|
|
display: flex;
|
|
}
|
|
}
|
|
}
|
|
|
|
@media (max-width: $phone-tiny) {
|
|
// Don't have a gap for the hosts selection
|
|
.nav-hosts-menu {
|
|
inset-inline-end: 0;
|
|
}
|
|
|
|
// Make the hosts selection fill the width of the page
|
|
.view-hosts .sidebar-hosts .pf-v6-c-page__sidebar-body {
|
|
inline-size: 100vw;
|
|
}
|
|
}
|
|
|
|
.navbar.navbar-pf-vertical {
|
|
border-block-start: 0;
|
|
}
|
|
|
|
.ct-switcher {
|
|
block-size: 100%;
|
|
|
|
> .pf-v6-c-select {
|
|
block-size: 100%;
|
|
padding: 0;
|
|
|
|
.pf-v6-c-select__toggle-text {
|
|
text-align: start;
|
|
}
|
|
}
|
|
|
|
.username {
|
|
display: inline-block;
|
|
}
|
|
|
|
.at {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.hostname {
|
|
display: block;
|
|
font-weight: var(--pf-t--global--font--weight--heading--default);
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
|
|
.ct-switcher-localonly {
|
|
padding-inline-start: var(--pf-t--global--spacer--lg);
|
|
padding-block-start: var(--pf-t--global--spacer--sm);
|
|
}
|
|
|
|
.pf-v6-c-select__toggle {
|
|
block-size: 100%;
|
|
}
|
|
|
|
.nav-action {
|
|
border-radius: var(--pf-t--global--border--radius--small);
|
|
padding-inline: var(--pf-t--global--spacer--control--horizontal--default);
|
|
align-items: center;
|
|
}
|
|
|
|
.host-apps {
|
|
flex: 1 1 0;
|
|
position: relative;
|
|
// HACK: 2 rem is a magic value as nav-icon-size is not sufficient
|
|
// Unclear yet where this height difference comes from
|
|
max-block-size: calc(100vh - 2rem - var(--nav-icon-size));
|
|
// In mobile, make the search at the top and the rest scroll
|
|
@media (max-width: $phone) {
|
|
display: grid;
|
|
grid-template-rows: auto 1fr;
|
|
position: sticky;
|
|
inset-block-start: 0;
|
|
margin-block: var(--pf-t--global--spacer--gutter--default);
|
|
|
|
> .search {
|
|
margin-inline: var(--pf-t--global--spacer--gutter--default);
|
|
inline-size: initial;
|
|
}
|
|
|
|
> .pf-v6-c-nav {
|
|
padding-inline: var(--pf-t--global--spacer--gutter--default);
|
|
overflow-y: auto;
|
|
}
|
|
}
|
|
}
|
|
|
|
.nav-system-menu {
|
|
overflow-x: hidden;
|
|
overflow-y: auto;
|
|
|
|
// Flow items, if wider (useful for mobile). Use 1fr (not auto) so every
|
|
// group's column tracks are equal-width; otherwise each group's grid sizes
|
|
// its columns to its own content and the second column fails to align
|
|
// between groups (e.g. System vs Tools).
|
|
.pf-v6-c-nav__list {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
|
}
|
|
|
|
// Give additional style to individual menu items
|
|
.pf-v6-c-nav__link {
|
|
display: grid;
|
|
grid-template: "name status" "match match";
|
|
grid-template-columns: 1fr auto;
|
|
justify-content: space-between;
|
|
gap: 0 var(--pf-t--global--spacer--md);
|
|
padding-inline-end: var(--pf-t--global--spacer--md);
|
|
// In mobile, sometimes items wrap; neighbors should also fill the space
|
|
block-size: 100%;
|
|
|
|
> .nav-item-name {
|
|
grid-area: name;
|
|
}
|
|
|
|
> .nav-item-status {
|
|
grid-area: status;
|
|
}
|
|
|
|
> .nav-item-hint {
|
|
grid-area: match;
|
|
}
|
|
|
|
> :empty {
|
|
display: none;
|
|
}
|
|
|
|
// Add a background to shine through the icon's gaps
|
|
// (for better contrast, even when hovering / selected)
|
|
.nav-item-status {
|
|
position: relative;
|
|
|
|
> svg {
|
|
// Set position for the icon sandwiching
|
|
position: relative;
|
|
// Move the icon up the stack
|
|
z-index: 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.view-hosts .sidebar-hosts {
|
|
.pf-v6-c-nav__list {
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.pf-v6-c-page__sidebar-body {
|
|
display: grid;
|
|
grid-template-rows: max-content max-content max-content;
|
|
|
|
@media (min-width: $desktop) {
|
|
max-block-size: 100%;
|
|
}
|
|
|
|
}
|
|
|
|
.pf-v6-c-nav {
|
|
overflow: auto;
|
|
// Don't run off the page when displaying many entries
|
|
// HACK: Unclear where the spacing comes from
|
|
max-block-size: calc(100vh - var(--pf-t--global--spacer--3xl) * 3);
|
|
}
|
|
|
|
.nav-hosts-actions {
|
|
display: grid;
|
|
gap: var(--pf-t--global--spacer--sm);
|
|
margin-block-start: var(--pf-t--global--spacer--md);
|
|
}
|
|
|
|
.nav-item {
|
|
display: grid;
|
|
grid-template-columns: 1fr auto;
|
|
border-radius: var(--pf-t--global--border--radius--small);
|
|
|
|
&:has(:hover, .pf-m-current) {
|
|
background-color: var(--pf-t--global--background--color--action--plain--hover);
|
|
}
|
|
|
|
&:has(.pf-m-current) {
|
|
/* Add a selected color */
|
|
border-inline-start: var(--pf-t--global--border--width--extra-strong) solid var(--ct-color-host-accent);
|
|
}
|
|
|
|
> .pf-v6-c-nav__link {
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
background-color: transparent;
|
|
}
|
|
}
|
|
|
|
.nav-item-actions {
|
|
display: flex;
|
|
gap: var(--pf-t--global--spacer--xs);
|
|
padding-inline: var(--pf-t--global--spacer--sm);
|
|
align-self: center;
|
|
}
|
|
}
|
|
|
|
.nav-item-actions:empty {
|
|
/* Nav item actions are part of the grid, whether they have content or not. If they do not have content, don't add extra spacing. */
|
|
display: none;
|
|
}
|
|
|
|
.ct-topnav-content {
|
|
margin-inline-start: auto;
|
|
}
|
|
|
|
// Rework navigation toggles in desktop and (especially) mobile modes
|
|
.super-user-indicator > button,
|
|
.ct-nav-toggle:not(.pf-v6-c-menu) {
|
|
// The normal margin to the right of the select dropdown makes them look uncentered in the masthead.
|
|
// https://github.com/patternfly/patternfly/issues/6632
|
|
.pf-v6-c-menu-toggle__toggle-icon {
|
|
margin-inline-end: 0;
|
|
}
|
|
|
|
// The way the event is handled, it checks for the button, but not the icon. It should bubble, but doesn't. We can work around this by passing mouse events through.
|
|
svg {
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
|
|
// In mobile view keep the non PF4 of displaying hover / focus state in the masthead / toolbar items
|
|
// This can go away once we move the host selector inside the Masthead
|
|
// https://github.com/patternfly/patternfly/issues/4524
|
|
@media (max-width: $phone) {
|
|
.ct-nav-toggle {
|
|
&:hover, &:active, &.active, &.interact, &[aria-expanded="true"], &.pf-m-expanded {
|
|
text-decoration: none;
|
|
|
|
.hostname {
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
|
|
&:focus {
|
|
text-decoration: none;
|
|
}
|
|
}
|
|
|
|
// Hide border from navigation items for mobile
|
|
.pf-v6-c-menu-toggle::before {
|
|
border: none;
|
|
}
|
|
|
|
// Hide local-only host switcher (i.e. when only showing the current user name)
|
|
.ct-switcher-localonly {
|
|
display: none !important;
|
|
}
|
|
}
|
|
|
|
.ct-locked .ct-lock-wrapper > svg {
|
|
margin-inline-end: var(--pf-t--global--spacer--sm);
|
|
color: var(--pf-t--global--icon--color--severity--moderate--default);
|
|
}
|
|
|
|
.super-user-indicator {
|
|
@media (min-width: $desktop) {
|
|
> .ct-locked {
|
|
.ct-lock-wrapper {
|
|
background: var(--pf-t--global--background--color--secondary--default);
|
|
border-radius: var(--pf-v6-c-button--BorderRadius);
|
|
padding-block: var(--pf-t--global--spacer--xs);
|
|
padding-inline: var(--pf-t--global--spacer--sm);
|
|
|
|
> svg {
|
|
color: var(--pf-t--global--icon--color--severity--moderate--default);
|
|
}
|
|
}
|
|
|
|
&:hover, &:focus {
|
|
background: none;
|
|
|
|
.ct-lock-wrapper {
|
|
background: var(--pf-t--global--background--color--secondary--hover);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
> .ct-unlocked {
|
|
&:hover, &:focus {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
svg {
|
|
display: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mobile sizes
|
|
@media (max-width: $phone) {
|
|
/* Mobile uses a light background, so use the darker variants for selected and hover */
|
|
.pf-v6-c-nav__link {
|
|
--pf-v6-c-nav__link--m-current--BackgroundColor: var(--pf-t--global--background--color--action--plain--clicked);
|
|
--pf-v6-c-nav__link--hover--BackgroundColor: var(--pf-t--global--background--color--action--plain--hover);
|
|
}
|
|
|
|
#host-toggle,
|
|
button.ct-nav-toggle {
|
|
// Remove gap between text and icon
|
|
--pf-v6-c-button--Gap: 0;
|
|
--pf-v6-c-menu-toggle--Gap: 0;
|
|
// Stretch to navbar
|
|
block-size: 100%;
|
|
// Don't stretch to fill content; make optimal width same as height
|
|
inline-size: var(--pf-t--global--spacer--4xl);
|
|
// Leave enough space for 5 items (4 + spinner)
|
|
max-inline-size: calc(100vw / 5);
|
|
display: grid !important;
|
|
grid-template-rows: 28px 1fr;
|
|
justify-content: center;
|
|
justify-items: center;
|
|
padding-inline: var(--pf-t--global--spacer--xs);
|
|
align-items: center;
|
|
|
|
// Remove the toggled outline
|
|
.pf-v6-c-menu-toggle::before {
|
|
display: none !important;
|
|
}
|
|
|
|
// Don't show toggle icon
|
|
.pf-v6-c-menu-toggle__icon {
|
|
margin-inline-end: 0;
|
|
}
|
|
|
|
.pf-v6-c-menu-toggle__image {
|
|
align-self: center;
|
|
margin: 0 !important;
|
|
}
|
|
|
|
// #host-sel specific
|
|
.pf-v6-c-select__toggle-arrow {
|
|
// This is here because Chrome is weird sometimes...
|
|
padding-block: 2px 5px;
|
|
padding-inline: 0;
|
|
}
|
|
|
|
> .pf-v6-c-select__toggle-wrapper {
|
|
flex: none;
|
|
max-inline-size: 100%;
|
|
}
|
|
}
|
|
|
|
// FIXME @Venefilyn: Some bug is causing the listener in SidebarToggle to trigger pointer clicks on internal elements causing the menu to not work.
|
|
// Setting it to initial means that the after content that covers up the whole button will still allow us to trigger it correctly.
|
|
#nav-system-item::after {
|
|
pointer-events: initial
|
|
}
|
|
|
|
// PF6 introduced toolbar column gap which is great in general. But not what we want for mobile view
|
|
.ct-topnav-content {
|
|
--pf-v6-c-toolbar__content-section--ColumnGap: 0;
|
|
}
|
|
}
|
|
|
|
/* Navigation animation */
|
|
|
|
@keyframes navHostsSlide {
|
|
0% { opacity: 0.25; transform: scaleY(0); }
|
|
25% { opacity: 0.5; transform: scaleY(0.25); }
|
|
100% { opacity: 1; transform: scaleY(1); }
|
|
}
|
|
|
|
@keyframes navHostsEditButtonsAppear {
|
|
0% { opacity: 0; max-block-size: 0; max-inline-size: 0; }
|
|
50% { opacity: 0; max-block-size: 0; max-inline-size: 0; }
|
|
75% { opacity: 0; max-block-size: 100%; max-inline-size: 100%; }
|
|
100% { opacity: 1; max-block-size: 100%; max-inline-size: 100%; }
|
|
}
|