mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
feat(mobile): redesign profile settings navigation
This commit is contained in:
1 parent
4b975c17dc
commit
5764482f2a
4 files changed
+235
-37
No files matched your search
@@ -100,7 +100,15 @@
|
||||
>
|
||||
<!-- Left navigation column -->
|
||||
<div class="profile-modal__menu" data-profile-mobile-menu aria-hidden="false">
|
||||
<nav class="profile-modal__nav" aria-label="Profile sections">
|
||||
<div class="profile-modal__menu-heading">
|
||||
<h3 id="profileModalMenuHeading" class="profile-modal__menu-title">
|
||||
Profile & settings
|
||||
</h3>
|
||||
<p class="profile-modal__menu-hint">
|
||||
Choose what you want to manage
|
||||
</p>
|
||||
</div>
|
||||
<nav class="profile-modal__nav" aria-labelledby="profileModalMenuHeading">
|
||||
<button
|
||||
id="profileNavAccount"
|
||||
type="button"
|
||||
@@ -108,6 +116,7 @@
|
||||
aria-controls="profilePaneAccount"
|
||||
aria-selected="true"
|
||||
data-state="active"
|
||||
data-mobile-description="Profile and account settings"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -130,6 +139,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneRelays"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Manage your relay connections"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -152,6 +162,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneWallet"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Manage your wallet and payments"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -173,6 +184,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneStorage"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Manage your stored files"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -195,6 +207,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneMyVideos"
|
||||
aria-selected="false"
|
||||
data-mobile-description="View and manage your videos"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -217,6 +230,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePanePlaylists"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Organize your playlists"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -239,6 +253,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneHashtags"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Explore and follow hashtags"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -261,6 +276,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneSubscriptions"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Manage your subscriptions"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -286,6 +302,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneFriends"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Find and manage your friends"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -308,6 +325,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneSafety"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Privacy and safety settings"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -330,6 +348,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneBlocked"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Manage blocked users"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -352,6 +371,7 @@
|
||||
class="btn-ghost focus-ring relative w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneMessages"
|
||||
aria-selected="false"
|
||||
data-mobile-description="View your messages"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -379,6 +399,7 @@
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneHistory"
|
||||
aria-selected="false"
|
||||
data-mobile-description="View your watch history"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -408,6 +429,7 @@
|
||||
class="btn-ghost focus-ring hidden w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||||
aria-controls="profilePaneAdmin"
|
||||
aria-selected="false"
|
||||
data-mobile-description="Moderate users and content"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
@@ -429,10 +451,12 @@
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
</button>
|
||||
</nav>
|
||||
<div class="profile-modal__mobile-actions" role="group" aria-label="Session actions">
|
||||
<button
|
||||
id="profileMobileLockNowBtn"
|
||||
type="button"
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium mt-6 lg:hidden hidden"
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium lg:hidden hidden"
|
||||
title="Forget your unlocked key on this device and re-lock storage + wallet now"
|
||||
>
|
||||
<svg
|
||||
@@ -453,7 +477,7 @@
|
||||
<button
|
||||
id="profileMobileLogoutBtn"
|
||||
type="button"
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium mt-2 lg:hidden"
|
||||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium lg:hidden"
|
||||
data-variant="critical"
|
||||
>
|
||||
<svg
|
||||
@@ -471,7 +495,7 @@
|
||||
</svg>
|
||||
Logout
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right content panes -->
|
||||
|
||||
+197
-31
@@ -767,6 +767,44 @@
|
||||
@apply btn-ghost focus-ring -mr-2 -mt-2 h-9 w-9 rounded-full p-0 text-lg leading-none;
|
||||
}
|
||||
|
||||
.site-tagline__text {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
@media (max-width: calc(theme("screens.md") - 0.02px)) {
|
||||
/* Both controls are fixed to the lower-right corner on phones. Keep the
|
||||
site-update notice above the search FAB so neither tap target obscures
|
||||
the other. */
|
||||
.site-update-notice {
|
||||
right: var(--space-lg);
|
||||
bottom: calc(
|
||||
var(--space-lg) + var(--space-2xl-plus) + var(--space-sm)
|
||||
);
|
||||
max-inline-size: min(
|
||||
var(--size-20rem),
|
||||
calc(100vw - (var(--space-lg) * 2))
|
||||
);
|
||||
}
|
||||
|
||||
.site-update-notice__button {
|
||||
min-block-size: var(--size-11);
|
||||
padding-inline: var(--space-md);
|
||||
padding-block: var(--space-sm);
|
||||
}
|
||||
|
||||
.site-tagline {
|
||||
margin-block: var(--space-lg) var(--space-xl);
|
||||
padding-inline: var(--space-md);
|
||||
}
|
||||
|
||||
.site-tagline .site-tagline__text {
|
||||
font-size: var(--font-size-lg);
|
||||
line-height: var(--line-height-tight);
|
||||
letter-spacing: var(--tracking-normal);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
}
|
||||
|
||||
.login-option::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@@ -3592,6 +3630,12 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
min-block-size: 0;
|
||||
}
|
||||
|
||||
.profile-modal__menu-heading,
|
||||
.profile-modal__mobile-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.profile-modal__pane-wrapper {
|
||||
@@ -3772,55 +3816,177 @@
|
||||
Chromium drops the whole block. `theme("screens.lg")` resolves to a literal at
|
||||
build time, which is valid. */
|
||||
@media (max-width: calc(theme("screens.lg") - 0.02px)) {
|
||||
.profile-modal__identity {
|
||||
align-self: stretch;
|
||||
max-inline-size: none;
|
||||
min-block-size: var(--size-6rem);
|
||||
margin-inline: var(--profile-modal-padding-inline);
|
||||
margin-block: 0;
|
||||
padding: var(--space-md);
|
||||
border: 0;
|
||||
border-radius: var(--radius-xl);
|
||||
color: var(--color-white);
|
||||
background:
|
||||
radial-gradient(
|
||||
100% 130% at 100% 0%,
|
||||
color-mix(in srgb, var(--color-accent) 82%, transparent),
|
||||
transparent 58%
|
||||
),
|
||||
linear-gradient(
|
||||
120deg,
|
||||
var(--color-text-strong) 0%,
|
||||
color-mix(in srgb, var(--color-text-strong) 82%, var(--color-accent)) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.profile-modal__identity-avatar {
|
||||
inline-size: var(--space-2xl-plus);
|
||||
block-size: var(--space-2xl-plus);
|
||||
border-color: var(--color-white);
|
||||
}
|
||||
|
||||
.profile-modal__identity-kicker {
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.profile-modal__identity-name {
|
||||
font-size: var(--font-size-lg);
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.profile-modal__menu {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
padding-block-end: var(--space-2xs);
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.profile-modal__menu-heading {
|
||||
display: block;
|
||||
padding-block: var(--space-xs) var(--space-sm);
|
||||
}
|
||||
|
||||
.profile-modal__menu-title {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 700;
|
||||
letter-spacing: var(--tracking-wide);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.profile-modal__menu-hint {
|
||||
margin: var(--space-3xs) 0 0;
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.profile-modal__nav {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
/* Every tile the SAME height, so a 2-line label can't stretch its row
|
||||
taller than the others (the "ragged grid of ellipses" fix). */
|
||||
grid-auto-rows: var(--profile-modal-nav-tile-height);
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.profile-modal__nav > button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
grid-template-columns: var(--space-2xl-plus) minmax(0, 1fr) var(--space-lg);
|
||||
grid-template-rows: auto auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
gap: var(--space-2xs);
|
||||
justify-content: start;
|
||||
column-gap: var(--space-md);
|
||||
row-gap: var(--space-3xs);
|
||||
width: 100%;
|
||||
block-size: 100%;
|
||||
/* Consistent rounded-square tiles, not rounded-full ellipses (btn's
|
||||
default) that turn into circles once a tile gets taller. */
|
||||
border-radius: var(--radius-xl);
|
||||
/* Subtle fill so they read as solid cards, not floating outlines. The
|
||||
active tile's bg-primary (higher specificity) still wins. */
|
||||
min-block-size: var(--size-5-75rem);
|
||||
text-align: start;
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 700;
|
||||
line-height: var(--line-height-tight);
|
||||
border-radius: var(--radius-lg);
|
||||
border-color: var(--color-border-subtle);
|
||||
background-color: var(--color-panel);
|
||||
padding-block: var(--space-xs);
|
||||
padding-inline: var(--space-3xs);
|
||||
line-height: 1.15;
|
||||
padding: var(--space-sm);
|
||||
overflow: hidden;
|
||||
/* wrap labels at spaces, not mid-word */
|
||||
overflow-wrap: break-word;
|
||||
word-break: normal;
|
||||
hyphens: none;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.profile-modal__nav > button[data-state="active"] {
|
||||
color: var(--color-text);
|
||||
border-color: color-mix(in srgb, var(--color-accent) 36%, transparent);
|
||||
background-color: color-mix(in srgb, var(--color-accent) 9%, var(--color-panel));
|
||||
box-shadow:
|
||||
inset var(--size-3px) 0 0 var(--color-accent),
|
||||
var(--shadow-md);
|
||||
}
|
||||
|
||||
.profile-modal__nav > button:focus-visible {
|
||||
outline-offset: calc(var(--space-3xs) * -1);
|
||||
}
|
||||
|
||||
/* slightly larger glyph for the tile */
|
||||
.profile-modal__nav > button svg {
|
||||
width: var(--space-lg);
|
||||
height: var(--space-lg);
|
||||
flex-shrink: 0;
|
||||
grid-column: 1;
|
||||
grid-row: 1 / span 2;
|
||||
width: var(--space-xl);
|
||||
height: var(--space-xl);
|
||||
padding: var(--space-sm);
|
||||
box-sizing: content-box;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-page-alt);
|
||||
}
|
||||
|
||||
.profile-modal__nav > button::before {
|
||||
content: "";
|
||||
grid-column: 3;
|
||||
grid-row: 1 / span 2;
|
||||
inline-size: var(--space-xs);
|
||||
block-size: var(--space-xs);
|
||||
border-block-start: var(--border-width-hairline) solid currentColor;
|
||||
border-inline-end: var(--border-width-hairline) solid currentColor;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.profile-modal__nav > button::after {
|
||||
content: attr(data-mobile-description);
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
align-self: start;
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: 400;
|
||||
line-height: var(--line-height-tight);
|
||||
}
|
||||
|
||||
.profile-modal__nav > button[data-state="active"] svg {
|
||||
color: var(--color-white);
|
||||
background-color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* In the tile grid the unread dot rides the tile's top-right corner instead
|
||||
of the vertical-nav left edge (where it landed in the grid gutter). */
|
||||
.profile-modal__nav > button .profile-nav__unread-dot {
|
||||
top: var(--space-2xs);
|
||||
top: var(--space-sm);
|
||||
left: auto;
|
||||
right: var(--space-2xs);
|
||||
right: calc(var(--space-sm) + var(--space-lg));
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.profile-modal__mobile-actions {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
gap: var(--space-xs);
|
||||
padding-block: var(--space-xs) var(--space-3xs);
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
color-mix(in srgb, var(--color-page) 0%, transparent),
|
||||
var(--color-page) var(--space-sm)
|
||||
);
|
||||
}
|
||||
|
||||
.profile-modal__mobile-actions > button {
|
||||
flex: 1 1 0;
|
||||
justify-content: center;
|
||||
min-block-size: var(--size-11);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: theme("screens.lg")) {
|
||||
|
||||
+2
-2
@@ -374,8 +374,8 @@
|
||||
|
||||
<!-- Tagline fade matches the cards. -->
|
||||
|
||||
<div class="fade-in fade-in-delay-300 mb-8 text-center">
|
||||
<h2 class="text-2xl font-bold tracking-wide text-muted-strong">
|
||||
<div class="site-tagline fade-in fade-in-delay-300 mb-8 text-center">
|
||||
<h2 class="site-tagline__text text-2xl font-bold tracking-wide text-muted-strong">
|
||||
seed. zap. subscribe.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -574,8 +574,16 @@ for (const _ of [0]) {
|
||||
const menuWrapper = document.querySelector('[data-profile-mobile-menu]');
|
||||
const paneWrapper = document.querySelector('[data-profile-mobile-pane]');
|
||||
const backButton = document.getElementById('profileModalBack');
|
||||
const mobileLockButton = document.getElementById('profileMobileLockNowBtn');
|
||||
const mobileLogoutButton = document.getElementById('profileMobileLogoutBtn');
|
||||
const mobileActions = mobileLogoutButton?.closest(
|
||||
'.profile-modal__mobile-actions',
|
||||
);
|
||||
|
||||
assert.ok(layout && menuWrapper && paneWrapper && backButton);
|
||||
assert.equal(mobileActions?.getAttribute('aria-label'), 'Session actions');
|
||||
assert.equal(mobileLockButton?.closest('nav'), null);
|
||||
assert.equal(mobileLogoutButton?.closest('nav'), null);
|
||||
|
||||
assert.equal(layout?.dataset.mobileView, 'menu');
|
||||
assert.equal(menuWrapper?.getAttribute('aria-hidden'), 'false');
|
||||
|
||||
Reference in new issue
Block a user