discourse/app/assets/stylesheets/common/base/menu-panel.scss
Jarek Radosz dc905cee48
UX: Fix tabs in new notification panel (#33871)
A followup to #33795

1. the hover effect was broken in Safari (because it was trying to
emulate a background by setting a box-shadow of a small child element)
<img width="45" height="47" alt="image"
src="https://github.com/user-attachments/assets/53deaa3c-d4f5-4b02-ad74-ec464cd12d38"
/>
2. in <= 400px height viewports elements were overlapping eachother and
icons moved on hover
<img width="40" height="64" alt="Screenshot 2025-07-26 at 12 17 21"
src="https://github.com/user-attachments/assets/950ef5f0-6d80-418f-b522-78a58730174f"
/>
3. changing `--user-menu-border-width` had issues: at 0px and at >= 3px
icons would move on hover, and increasing the value would make tabs
overlap with the border
<img width="49" height="93" alt="image"
src="https://github.com/user-attachments/assets/4e514f02-437c-4bfd-8d98-f0dffdfccac5"
/>
2025-07-29 21:25:36 +02:00

848 lines
16 KiB
SCSS
Vendored

@use "lib/viewport";
:root {
--d-unread-notification-background: light-dark(
var(--tertiary-25),
var(--tertiary-200)
);
--active-shadow: 0 0 0 0.5rem var(--d-sidebar-active-background);
--hover-shadow: 0 0 0 0.5rem var(--d-sidebar-highlight-background);
}
.menu-panel.slide-in {
position: fixed;
right: 0;
top: 0;
box-sizing: border-box;
// ensure there's always space to click outside on tiny devices
max-width: 90vw;
box-shadow: 0 0 30px -2px rgb(0, 0, 0, 0.5);
height: 100dvh;
.panel-body {
width: 100%;
}
}
.header-cloak {
height: 100%;
width: 100%;
position: fixed;
background-color: rgb(0, 0, 0, 0.3);
top: 0;
left: 0;
display: none;
touch-action: pan-y pinch-zoom;
}
.menu-panel.drop-down {
position: absolute;
// positions are relative to the .d-header .panel div
top: 100%; // directly underneath .panel
right: 5px;
max-height: calc(100dvh - var(--header-offset) - 1em);
border-radius: var(--d-border-radius);
.panel-body {
// improves usability on high zoom levels
max-width: calc(100vw - 2em);
}
@include float-down;
}
.menu-panel {
--shadow-dropdown: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
--list-item-padding: 0.5em 1rem;
border: 1px solid var(--content-border-color);
box-shadow: var(--shadow-dropdown);
background-color: var(--secondary);
z-index: z("header");
padding: 0.5em;
width: 320px;
overflow: hidden;
display: flex;
flex-direction: column;
box-sizing: border-box;
max-height: 100vh;
// remove once glimmer search menu in place
.widget-link,
.categories-link {
padding: 0.25em 0.5em;
display: block;
color: var(--primary);
&:hover,
&:focus {
background-color: var(--d-hover);
outline: none;
}
.d-icon {
color: var(--primary-medium);
}
.new {
font-size: var(--font-down-1);
margin-left: 0.5em;
color: var(--primary-med-or-secondary-med);
}
&.show-help,
&.filter {
color: var(--tertiary);
}
}
.search-link,
.categories-link {
padding: 0.25em 0.5em;
display: block;
color: var(--primary);
&:hover,
&:focus {
background-color: var(--d-hover);
outline: none;
}
.d-icon {
color: var(--primary-medium);
}
.new {
font-size: var(--font-down-1);
margin-left: 0.5em;
color: var(--primary-med-or-secondary-med);
}
&.show-help,
&.filter {
color: var(--tertiary);
}
}
li.category-link {
float: left;
background-color: transparent;
display: inline-flex;
align-items: center;
padding: 0.25em 0.5em;
width: 50%;
box-sizing: border-box;
a {
display: inline-flex;
&:hover,
&:focus {
background: transparent;
.category-name {
color: var(--primary);
}
}
}
.badge-notification {
color: var(--primary-med-or-secondary-med);
background-color: transparent;
display: inline;
padding: 0;
font-size: var(--font-down-1);
line-height: var(--line-height-large);
}
}
// note these topic counts only appear for anons in the category hamburger drop down
b.topics-count {
color: var(--primary-med-or-secondary-med);
font-weight: normal;
font-size: var(--font-down-1);
}
div.discourse-tags {
font-size: var(--font-down-1);
}
.sidebar-panel-header__row {
width: 100%;
}
.sidebar-sections {
&__back-to-forum {
color: var(--d-sidebar-link-color);
display: flex;
align-items: center;
svg {
margin-right: var(--d-sidebar-section-link-prefix-margin-right);
height: 0.75em;
width: 0.75em;
color: var(--d-sidebar-link-icon-color);
}
}
}
hr {
margin: 0;
height: auto;
}
.panel-header {
position: absolute;
right: 20px;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
.panel-body {
display: flex;
touch-action: pan-y pinch-zoom;
overflow: hidden;
height: 100%;
}
.panel-body-contents {
max-height: 100%;
width: 100%;
display: flex;
flex-direction: column;
// 2em padding very useful for iOS Safari's overlayed bottom nav
// padding-bottom: calc(env(safe-area-inset-bottom) + 2em);
}
.panel-body-bottom {
display: flex;
flex: 1 0 0;
flex-wrap: wrap;
padding: 0.75em 0.75em max(env(safe-area-inset-bottom), 0.75em);
.show-all {
display: flex;
flex: 1 1 auto;
button {
width: 100%;
}
}
.notifications-dismiss {
margin-left: 0.5em;
}
}
.badge-notification {
vertical-align: text-bottom;
}
.sidebar-filter {
width: 100%;
}
.sidebar-search {
width: 100%;
}
.empty-state {
padding: 0.5em 1em;
}
}
.search-menu .menu-panel {
width: 500px;
}
.user-menu.revamped {
--user-menu-border-width: 1px;
right: 0;
padding: 0;
width: unset;
@include viewport.until(sm) {
width: 320px;
}
.panel-body-bottom {
flex: 0;
}
.menu-tabs-container {
display: flex;
flex-direction: column;
overflow-y: auto;
overscroll-behavior: contain;
}
.tabs-list {
display: flex;
flex-direction: column;
.btn {
display: flex;
position: relative;
border-radius: 0;
padding: 0.375em; // 6px
@include viewport.until(sm) {
padding: 0.75em 0.875em; // 12px 14px
}
@media screen and (height <= 400px) {
// helps with 400% zoom level
font-size: var(--font-down-1);
padding: 0.125em 0.375em; // 2px 6px
}
.d-icon {
border-radius: var(--d-border-radius);
color: var(--primary-medium);
padding: 0.5em; // 8px
}
.badge-notification {
background-color: var(--tertiary-med-or-tertiary);
position: absolute;
right: 6px;
top: 6px;
font-size: var(--font-down-3);
@media screen and (height <= 400px) {
// helps with 400% zoom level
right: 3px;
top: 0;
}
}
&.active {
.d-icon {
color: var(--d-sidebar-active-color);
background-color: var(--d-sidebar-active-background);
}
}
&:not(.active):hover,
&:not(.active):focus-visible {
background: none;
.d-icon {
background-color: var(--d-sidebar-highlight-background);
}
}
}
}
.bottom-tabs {
border-top: var(--user-menu-border-width) solid var(--content-border-color);
}
.panel-body-contents {
display: flex;
flex-direction: row-reverse;
@include viewport.until(sm) {
.quick-access-panel li {
padding-block: 0.15em;
}
}
}
.quick-access-panel {
width: 320px;
justify-content: space-between;
box-sizing: border-box;
border-right: var(--user-menu-border-width) solid
var(--content-border-color);
min-width: 0; // makes sure menu tabs don't go off screen
}
#quick-access-profile {
display: inline;
ul {
flex-wrap: nowrap;
height: 100%;
align-items: center;
overflow-y: auto; // really short viewports
}
li {
flex: 1 1 auto;
display: flex;
flex-direction: column;
max-height: 3.25em;
> * {
// button, a, and everything else
align-items: center;
margin: 0;
padding: var(--list-item-padding);
flex: 1 1 auto;
}
img.emoji {
height: 1em;
width: 1em;
padding-top: 0.2em;
margin-right: 0.5em;
}
.d-icon {
padding-top: 0;
}
&.enabled .d-icon {
color: var(--tertiary);
}
}
.set-user-status {
.emoji {
padding-top: 0;
}
}
.profile-tab-btn {
justify-content: unset;
line-height: var(--line-height-large);
width: 100%;
.relative-date {
font-size: var(--font-down-3);
color: var(--primary-medium);
}
.d-icon {
padding: 0;
}
}
.do-not-disturb {
.d-icon-toggle-on {
color: var(--tertiary);
}
}
hr {
border-top: var(--user-menu-border-width) solid
var(--content-border-color);
width: 100%;
}
}
}
.hamburger-panel {
.panel-body {
overflow-y: auto;
}
@include viewport.until(sm) {
.sidebar-section-wrapper {
margin-bottom: 1.2em;
}
}
@include viewport.from(sm) {
// sidebar-hamburger hybrid
.revamped {
--d-sidebar-row-height: 1.875rem;
width: 22.5rem;
.panel-body-content {
width: 100%;
min-width: 0; // prevent content blowing out width
}
.sidebar-section-wrapper {
.sidebar-section-header-button {
opacity: 1;
}
.sidebar-section-link.active {
font-weight: normal;
background: var(--d-hover);
}
.sidebar-section-header-wrapper .select-kit .btn:hover {
background: transparent;
}
&.sidebar-section {
padding-top: 0.5em;
.sidebar-section-header-wrapper {
margin: 0 0 0.5em;
padding-bottom: 0.25em;
}
.sidebar-section-content {
display: grid;
grid-template-columns: 1fr 1fr;
li {
min-width: 0;
}
}
}
}
.sidebar-section-link-wrapper
.sidebar-section-link-hover:hover
.sidebar-section-hover-button {
background: transparent;
}
.sidebar-footer-wrapper {
padding: 0;
margin-top: 1em;
.sidebar-footer-container {
padding: 0;
border: none;
background: var(--secondary);
&::before {
top: -1.5em;
background: linear-gradient(
to bottom,
transparent,
rgb(var(--secondary-rgb), 1)
);
}
}
}
}
}
}
.menu-links.columned {
li {
width: 50%;
float: left;
}
}
// Panel / user-notification-list styles. **not** menu panel sizing styles
.user-menu .quick-access-panel,
.user-notifications-list {
width: 100%;
display: flex;
flex-direction: column;
min-height: 0;
max-height: 100%;
/* as a big ol' click target, don't let text inside be selected */
@include unselectable;
&:focus {
outline: none;
}
.double-user,
.multi-user {
white-space: unset;
}
.item-label {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: var(--primary);
}
h3 {
padding: 0 0.4em;
font-weight: bold;
margin: 0.5em 0;
}
ul {
display: flex;
flex-flow: column wrap;
overflow: hidden;
max-height: 100%;
}
.user-menu-button-profile {
li svg {
color: var(--d-button-flat-icon-color);
}
li:hover svg {
color: var(--d-button-flat-icon-color--hover);
}
.item-label {
font-weight: normal;
}
}
li {
box-sizing: border-box;
list-style-type: none;
&.unread,
&.pending {
background-color: var(--d-unread-notification-background);
}
&:hover {
background-color: var(--d-hover);
outline: none;
}
&:focus-within {
background: var(--d-hover);
a {
// we don't need the link focus because we're styling the parent
outline: 0;
}
.btn-flat:focus {
// undo default btn-flat style
background: transparent;
}
}
// This is until other languages remove the HTML from within
// notifications. It can then be removed
div .fa {
display: none;
}
span.double-user,
// e.g., "username, username2"
span.multi-user
// e.g., "username and n others"
{
display: inline;
max-width: 100%;
align-items: baseline;
white-space: nowrap;
}
span.multi-user
// e.g., "username, username2, and n others"
{
span.multi-username:nth-of-type(2) {
// margin between username2 and "and n others"
margin-right: 0.25em;
}
}
// truncate when usernames are very long
span.multi-username {
@include ellipsis;
flex: 0 1 auto;
min-width: 1.2em;
max-width: 10em;
&:nth-of-type(2) {
// margin for comma between username and username2
margin-left: 0.25em;
}
}
a {
display: flex;
padding: var(--list-item-padding);
.item-label {
font-weight: bold;
}
.item-description {
color: var(--primary);
overflow: hidden; // clears the text from wrapping below icons
overflow-wrap: break-word;
@include line-clamp(2);
}
}
a,
button {
> div {
display: flex;
flex-direction: column;
overflow: hidden;
}
}
p {
margin: 0;
overflow: hidden;
}
}
li:not(.show-all) {
padding: 0;
align-self: flex-start;
width: 100%;
.d-icon {
padding-top: 0.2em;
margin-right: 0.5em;
}
}
.is-warning {
.d-icon-envelope {
color: var(--danger);
}
}
.read,
.bookmark {
background-color: var(--secondary);
a .d-icon {
color: var(--primary-medium);
}
}
.none {
padding-top: 5px;
}
.spinner-container {
min-height: 2em;
margin: auto;
}
.spinner {
width: 20px;
height: 20px;
border-width: 2px;
margin: 0 auto;
}
.show-all a {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
min-height: 30px;
color: var(--primary-med-or-secondary-high);
background: var(--blend-primary-secondary-5);
&:hover {
color: var(--primary);
background: var(--primary-low);
}
}
}
// Styles to have user avatar positioned and sized correctly
.user-menu.show-avatars,
.user-notifications-list.show-avatars {
li.notification,
li.bookmark,
li.reviewable,
li.message {
a {
color: var(--primary);
align-items: center;
.item-label {
font-weight: bold;
}
.item-description {
color: var(--primary);
@include line-clamp(2);
}
.icon-avatar {
display: flex;
position: relative;
overflow: visible;
flex-shrink: 0;
width: 2em;
height: 2em;
margin: 0.3em 1em auto 0;
.avatar {
width: 100%;
height: 100%;
}
&__icon-wrapper {
position: absolute;
right: -0.65em;
top: -0.45em;
display: flex;
justify-content: center;
align-items: center;
width: 22px;
height: 22px;
border-radius: 100%;
background: var(--secondary);
.d-icon {
display: block;
margin: 0;
padding: 0;
font-size: var(--font-down-1);
color: var(--primary);
}
}
}
& + div {
padding: 0.25em 0;
}
}
&.unread,
&.pending {
.icon-avatar__icon-wrapper {
background: var(--tertiary);
.d-icon {
color: var(--secondary);
}
}
}
}
}
.hamburger-panel .menu-panel.slide-in {
left: 0;
.panel-body {
display: block;
}
.panel-body-contents {
max-height: unset;
min-height: 100%;
}
}
.sidebar-section-header {
.sidebar-section-header-global-indicator {
.d-icon {
color: var(--d-sidebar-header-icon-color);
}
}
}
.sidebar-hamburger-dropdown {
.discourse-no-touch & {
.sidebar-section-wrapper .sidebar-section-header-wrapper:hover,
.sidebar-section-wrapper .sidebar-section-header-wrapper:focus-within {
background: transparent;
}
}
}