Kris 19aed18d4f
UX: hide user card focus until keyboard navigation begins (#31014)
When the usercard is opened, we manually assign focus to the first link
to capture focus, but this creates an undesirable focus ring for
visitors not relying on focus indicators for navigation.

With this update, the focus is assigned to the container rather than the
first link. This fulfills the goal of capturing focus within the user
card, but hides the focus ring until navigation within the card begins.

`tabindex="-1"` is necessary because this allows us to manually focus
the container (normally unfocusable) while removing it from the tab
order for subsequent navigation.

Before:


![image](https://github.com/user-attachments/assets/1da3a4bb-181b-4cf6-815b-66199757e300)



After:


![image](https://github.com/user-attachments/assets/dda06321-8558-46bf-b16b-dead68e48b92)

then on tab: 


![image](https://github.com/user-attachments/assets/c4cc3d7b-faaf-4bc0-a7c9-d6afef4c6009)

---------

Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
2025-01-27 12:34:04 -05:00

394 lines
6.5 KiB
SCSS

@use "sass:math";
.fk-d-menu[data-identifier="usercard"] {
max-width: calc(100vw - 2em);
width: auto;
.fk-d-menu__inner-content {
min-width: 0;
overflow: visible;
}
}
.user-card,
.group-card {
--card-width: 39em;
--avatar-width: 8em;
--avatar-margin: -3.3em; // extends the avatar above the card
}
.animated-placeholder {
height: 20px;
position: relative;
}
.card-avatar-placeholder {
width: var(--avatar-width);
height: var(--avatar-width);
border-radius: 100%;
position: relative;
overflow: hidden;
&::before {
@media (prefers-reduced-motion: no-preference) {
animation: placeHolderShimmer 4s linear infinite forwards;
}
position: absolute;
left: 0;
content: "";
background: linear-gradient(
to right,
var(--primary-very-low) 10%,
var(--primary-low) 18%,
var(--primary-very-low) 33%
);
height: var(--avatar-width);
width: var(--card-width);
}
}
// shared styles for user and group cards
.user-card,
.group-card {
min-width: 0;
width: var(--card-width);
color: var(--primary);
background: var(--secondary) center center;
background-size: cover;
position: unset !important;
margin: 0 !important;
border-radius: 0 !important;
box-shadow: unset !important;
z-index: unset !important;
.card-content {
padding: 10px;
background: rgba(var(--secondary-rgb), 0.85);
&::after {
content: "";
display: block;
clear: both;
}
a.card-huge-avatar {
display: block;
}
.bio {
@include line-clamp(2);
}
}
.card-row:not(.first-row) {
margin-top: 0.5em;
}
// avatar - names - controls
.first-row {
.names {
padding-left: 1.25em;
.user-profile-link {
display: flex;
align-items: center;
outline: none;
}
.d-icon {
margin: 0 0.25em;
}
.name-username-wrapper {
margin-right: 0;
flex: 0 1 auto;
@include ellipsis;
}
span {
display: block;
}
}
.usercard-controls {
list-style-type: none;
margin: 0;
button {
width: 100%;
}
}
}
.btn {
margin-bottom: 5px;
}
.names__primary {
line-height: var(--line-height-medium);
font-size: var(--font-up-5);
font-weight: bold;
.d-icon {
color: var(--primary);
}
}
.names__secondary {
font-size: var(--font-up-1);
}
.metadata {
display: flex;
flex-wrap: wrap;
gap: 0.15em 0.5em;
color: var(--primary);
&.email,
.desc,
a {
color: var(--primary-high);
}
}
.names__secondary,
[class*="metadata__"] {
margin: 0;
@include ellipsis;
}
.names__primary,
.names__secondary {
a {
color: var(--primary);
}
}
p {
margin: 0 0 5px 0;
}
}
// styles for user cards only
.user-card {
&:focus-visible {
// we move focus to the card, but the outline isn't necessary until keyboard nav starts within
outline: none;
}
// avatar - names - controls
.first-row {
display: flex;
.avatar-placeholder {
width: var(--avatar-width);
height: var(--avatar-width);
}
.user-card-avatar {
margin-top: var(--avatar-margin);
max-height: var(--avatar-width);
}
.avatar {
width: var(--avatar-width);
height: var(--avatar-width);
}
.new-user a {
color: var(--primary-low-mid);
}
}
// user bio - suspension reason
.second-row {
max-height: 150px;
overflow: auto;
.bio {
a:not(.mention) {
color: var(--tertiary);
}
.overflow {
max-height: 60px;
overflow: hidden;
}
}
.suspended {
color: var(--danger);
.suspension-reason-title,
.suspension-date {
font-weight: bold;
}
}
.profile-hidden,
.inactive-user {
font-size: var(--font-up-1);
margin-top: 0.5em;
}
}
// featured topic
.featured-topic {
.desc {
color: var(--primary-high);
}
a {
color: var(--primary);
text-decoration: underline;
}
}
// location and website
.location-and-website {
display: flex;
flex-wrap: wrap;
width: 100%;
align-items: center;
.location,
.website-name {
display: flex;
overflow: hidden;
align-items: center;
.d-icon {
margin-right: 0.25em;
}
}
.website-name a,
.location span {
@include ellipsis;
color: var(--primary);
}
.location,
.local-time,
.website-name {
margin-right: 0.5em;
}
.website-name a {
text-decoration: underline;
}
}
// custom user fields
.public-user-fields {
margin: 0;
.user-field-value-list-item:not(:last-of-type) {
&::after {
// create comma separated list
content: ",";
}
}
}
// badges
.badge-section {
line-height: 0;
.user-badge {
@include ellipsis;
background: var(--primary-very-low);
border: 1px solid var(--primary-low);
color: var(--primary);
}
.user-card-badge-link {
overflow: hidden;
}
.user-card-badge-link,
.more-user-badges {
vertical-align: top;
display: inline-block;
}
.more-user-badges a {
@extend .user-badge;
}
}
.user-status {
display: flex;
img.emoji {
margin-bottom: 1px;
margin-right: 0.3em;
}
.relative-date {
flex: 1 0 auto;
text-align: left;
font-size: var(--font-down-3);
padding-top: 0.5em;
margin-left: 0.6em;
color: var(--primary-medium);
}
&__description {
@include ellipsis;
}
}
}
// styles for group cards only
.group-card {
// avatar - names and controls
.first-row {
display: flex;
.group-card-avatar {
margin-top: var(--avatar-margin);
}
.avatar-flair {
display: flex;
background-size: contain;
background-repeat: no-repeat;
width: var(--avatar-width);
height: var(--avatar-width);
color: var(--primary);
.d-icon {
margin: auto;
font-size: calc(var(--avatar-width) / 1.5);
}
&.rounded {
border-radius: 50%;
}
}
}
// group bio
.second-row {
max-height: 150px;
overflow: auto;
.bio {
a:not(.mention) {
color: var(--tertiary);
}
img {
max-width: 100%;
height: auto;
}
.overflow {
max-height: 60px;
overflow: hidden;
}
}
}
}