Style overhaul.

This commit is contained in:
2026-02-11 01:18:19 +00:00
parent a944224027
commit 4ebe5ae968
17 changed files with 234 additions and 121 deletions
+1 -10
View File
@@ -1,5 +1,5 @@
<template>
<section class="view-root host-view" data-view="host-profile">
<section class="view-root view-root--wide host-view" data-view="host-profile">
<header class="view-header">
<h1>{{ headingTitle }}</h1>
<p class="view-lede">{{ subheading }}</p>
@@ -125,12 +125,3 @@ const handleButtonRowKeydown = (event) => {
// Down does nothing (no elements below to navigate to)
}
</script>
<style scoped>
.host-view { padding: 3rem 1.5rem 4rem; }
.host-actions { display: flex; flex-direction: column; gap: 0.75rem; }
.host-actions .button-row { gap: 0.75rem; flex-wrap: wrap; }
.host-actions .button-row button { flex: 1 1 0; }
.note { margin: 0; color: var(--color-text-muted); }
.empty-state { margin: 0; color: var(--color-text-muted); }
</style>
+2 -7
View File
@@ -27,17 +27,12 @@ defineProps({
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid var(--color-border);
border-top: 4px solid var(--color-primary);
border: 3px solid var(--color-border);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-container p {
color: var(--color-text-muted);
margin: 0;
-8
View File
@@ -84,12 +84,4 @@ function handleCancel() {
flex-direction: column;
gap: var(--space-md);
}
.error {
color: var(--color-danger-text);
}
.small {
font-size: 0.9rem;
}
</style>
+16 -12
View File
@@ -1,15 +1,17 @@
<template>
<section class="view-root" data-view="profile">
<div class="theme-toggle">
<ThemeSelector />
<section class="view-root view-root--profile" data-view="profile">
<div class="view-header-wrapper">
<div class="theme-toggle">
<ThemeSelector />
</div>
<header class="view-header">
<h1>User Profile</h1>
<Breadcrumbs ref="breadcrumbs" :entries="breadcrumbEntries" @keydown="handleBreadcrumbKeydown" />
<p class="view-lede">Account dashboard for managing credentials and authenticating with other devices.</p>
</header>
</div>
<header class="view-header">
<h1>User Profile</h1>
<Breadcrumbs ref="breadcrumbs" :entries="breadcrumbEntries" @keydown="handleBreadcrumbKeydown" />
<p class="view-lede">Account dashboard for managing credentials and authenticating with other devices.</p>
</header>
<section class="section-block" ref="userInfoSection">
<section class="section-block section-block--constrained" ref="userInfoSection">
<UserBasicInfo
v-if="authStore.userInfo?.ctx"
ref="userBasicInfo"
@@ -38,7 +40,7 @@
</UserBasicInfo>
</section>
<section class="section-block">
<section :class="['section-block', { 'section-block--constrained': !useWideLayout }]">
<div class="section-header">
<h2>Your Passkeys</h2>
<p class="section-description">Ideally have at least two passkeys in case you lose one. More than one user can be registered on the same device, giving you a choice at login. <a href="https://bitwarden.com/pricing/" target="_blank" rel="noopener noreferrer">Bitwarden</a> can sync one passkey to all your devices. Other secure options include <b>local passkeys</b>, as well as hardware keys such as <a href="https://www.yubico.com" target="_blank" rel="noopener noreferrer">YubiKey</a>. Cloud sync via Google, Microsoft or iCloud is discouraged.</p>
@@ -70,6 +72,7 @@
:terminating-sessions="terminatingSessions"
:hovered-credential-uuid="hoveredCredentialUuid"
:navigation-disabled="hasActiveModal"
:section-class="useWideLayout ? '' : 'section-block--constrained'"
@terminate="terminateSession"
@session-hover="hoveredSession = $event"
@navigate-out="handleSessionNavigateOut"
@@ -88,7 +91,7 @@
</form>
</Modal>
<section class="section-block">
<section :class="['section-block', { 'section-block--constrained': !useWideLayout }]">
<div class="button-row" ref="logoutButtons">
<button
type="button"
@@ -333,6 +336,8 @@ const isAdmin = computed(() => {
return perms.includes('auth:admin') || perms.includes('auth:org:admin')
})
const hasMultipleSessions = computed(() => sessions.value.length > 1)
const credentials = computed(() => authStore.userInfo?.credentials || [])
const useWideLayout = computed(() => sessions.value.length > 4 || credentials.value.length > 4)
const breadcrumbEntries = computed(() => { const entries = [{ label: 'Auth', href: makeUiHref() }]; if (isAdmin.value) entries.push({ label: 'Admin', href: adminUiPath() }); return entries })
const saveName = async () => {
@@ -350,7 +355,6 @@ const saveName = async () => {
</script>
<style scoped>
.view-lede { margin: 0; color: var(--color-text-muted); font-size: 1rem; }
.section-header { display: flex; flex-direction: column; gap: 0.4rem; }
.empty-state { margin: 0; color: var(--color-text-muted); text-align: center; padding: 1rem 0; }
.logout-note { margin: 0.75rem 0 0; color: var(--color-text-muted); font-size: 0.875rem; }
+13 -10
View File
@@ -55,15 +55,14 @@
<p class="device-permit-text">Permit {{ deviceInfo.action === 'register' ? 'registration' : 'login' }} to <strong>{{ deviceInfo.host }}</strong></p>
<p class="device-meta">{{ deviceInfo.user_agent_pretty || '—' }}</p>
<p v-if="error" class="error-message" style="margin-top: 0.5rem;">{{ error }}</p>
<p v-if="error" class="error-message">{{ error }}</p>
<div class="button-row" style="margin-top: 0.75rem; display: flex; gap: 0.5rem;">
<div class="button-row device-actions">
<button
type="button"
class="btn-secondary"
:disabled="loading"
@click="deny"
style="flex: 1;"
>
Deny
</button>
@@ -72,7 +71,6 @@
type="submit"
:disabled="loading"
class="btn-primary"
style="flex: 1;"
>
{{ loading ? 'Authenticating…' : 'Authorize' }}
</button>
@@ -921,10 +919,6 @@ defineExpose({ reset, deny, code, handleInput, loading, error })
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.device-info {
display: flex;
flex-direction: column;
@@ -945,9 +939,18 @@ defineExpose({ reset, deny, code, handleInput, loading, error })
}
.error-message {
margin: 0;
margin: 0.5rem 0 0;
font-size: 0.875rem;
color: var(--color-error, #ef4444);
margin-bottom: 1rem;
}
.device-actions {
margin-top: 0.75rem;
display: flex;
gap: 0.5rem;
}
.device-actions button {
flex: 1;
}
</style>
@@ -8,7 +8,7 @@
<!-- Error state -->
<div v-else-if="error" class="error-section">
<p class="error-message">{{ error }}</p>
<button class="btn-primary" @click="retry" style="margin-top: 0.75rem;">Try Again</button>
<button class="btn-primary" @click="retry">Try Again</button>
</div>
<!-- Connecting phase -->
@@ -293,10 +293,6 @@ defineExpose({ retry, cancel })
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.auth-display {
display: flex;
flex-direction: column;
@@ -499,6 +495,10 @@ defineExpose({ retry, cancel })
color: var(--color-error, #ef4444);
}
.error-section button {
margin-top: 0.75rem;
}
/* Responsive adjustments */
@media (max-width: 640px) {
.auth-content {
+2 -3
View File
@@ -1,6 +1,6 @@
<template>
<div class="app-shell">
<div v-if="status.show" class="global-status" style="display: block;">
<div v-if="status.show" class="global-status show">
<div :class="['status', status.type]">
{{ status.message }}
</div>
@@ -18,7 +18,7 @@
<div class="section-body center">
<!-- Local passkey authentication view -->
<div v-if="authView === 'local'" class="auth-view">
<div class="button-row center" ref="buttonRow">
<div class="button-row button-row--center" ref="buttonRow">
<slot name="actions"
:loading="loading"
:can-authenticate="canAuthenticate"
@@ -284,7 +284,6 @@ defineExpose({
</script>
<style scoped>
.button-row.center { display: flex; justify-content: center; gap: 0.75rem; flex-wrap: wrap; }
.user-line { margin: 0.5rem 0 0; font-weight: 500; color: var(--color-text); }
main.view-root { min-height: 100vh; align-items: center; justify-content: center; padding: 2rem 1rem; }
.surface.surface--tight {
+2 -1
View File
@@ -1,5 +1,5 @@
<template>
<section class="section-block" data-component="session-list-section">
<section :class="['section-block', sectionClass]" data-component="session-list-section">
<div class="section-header">
<h2>Active Sessions</h2>
<p class="section-description">{{ sectionDescription }}</p>
@@ -75,6 +75,7 @@ const props = defineProps({
terminatingSessions: { type: Object, default: () => ({}) },
hoveredCredentialUuid: { type: String, default: null },
navigationDisabled: { type: Boolean, default: false },
sectionClass: { type: String, default: '' },
})
const emit = defineEmits(['terminate', 'sessionHover', 'navigate-out'])
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<div v-if="authStore.status.show" class="global-status" style="display: block;">
<div v-if="authStore.status.show" class="global-status show">
<div :class="['status', authStore.status.type]">
{{ authStore.status.message }}
</div>