mirror-cockpit-project-cockpit/pkg/shell/nav.scss
Peter Buchan Jr d3488cc1a3 shell: Don't style the System button label as a nav toggle on mobile
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
2026-10-21 09:52:42 +02:00

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%; }
}