refactor: centralize UI config and inject CSS variables dynamically

This commit is contained in:
2026-02-28 04:18:53 +00:00
parent d5d3d37804
commit 70d7c8873e
8 changed files with 29 additions and 22 deletions

View File

@@ -6,6 +6,7 @@ import Footer from './components/Footer.vue'
import Sidebar from './components/Sidebar.vue' import Sidebar from './components/Sidebar.vue'
import InstallPrompt from './components/InstallPrompt.vue' import InstallPrompt from './components/InstallPrompt.vue'
import ReloadPrompt from './components/ReloadPrompt.vue' import ReloadPrompt from './components/ReloadPrompt.vue'
import { UI_CONFIG } from './config/ui'
const isSidebarOpen = ref(window.innerWidth >= 768) const isSidebarOpen = ref(window.innerWidth >= 768)
const router = useRouter() const router = useRouter()
@@ -31,6 +32,11 @@ const handleResize = () => {
} }
onMounted(() => { onMounted(() => {
// Set global CSS variables from config
document.documentElement.style.setProperty('--header-height', `${UI_CONFIG.headerHeight}px`)
document.documentElement.style.setProperty('--footer-height', `${UI_CONFIG.footerHeight}px`)
document.documentElement.style.setProperty('--page-padding', `${UI_CONFIG.pagePadding}px`)
window.addEventListener('resize', handleResize) window.addEventListener('resize', handleResize)
}) })
@@ -70,14 +76,14 @@ onUnmounted(() => {
padding: 1rem; padding: 1rem;
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
/* Space for fixed footer on mobile + extra margin (match top padding 2rem + footer height ~40px) */ /* Space for fixed footer on mobile + extra margin */
padding-bottom: calc(1rem + 40px + env(safe-area-inset-bottom)); padding-bottom: calc(1rem + var(--footer-height) + env(safe-area-inset-bottom));
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.main-content { .main-content {
padding: 0.5rem; padding: 0.5rem;
padding-bottom: calc(0.5rem + 40px + env(safe-area-inset-bottom)); padding-bottom: calc(0.5rem + var(--footer-height) + env(safe-area-inset-bottom));
} }
} }

View File

@@ -14,7 +14,8 @@ const version = __APP_VERSION__;
<style scoped> <style scoped>
.app-footer { .app-footer {
width: 100%; width: 100%;
padding: 0.5rem; height: var(--footer-height);
padding: 0 0.5rem;
/* Background handled by glass-panel */ /* Background handled by glass-panel */
border-left: none; border-left: none;
border-right: none; border-right: none;
@@ -24,12 +25,9 @@ const version = __APP_VERSION__;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 10; z-index: 10;
/* Remove fixed height to allow content to dictate size */
/* height: 30px; */
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 0; left: 0;
padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
} }
@media (min-width: 768px) { @media (min-width: 768px) {

View File

@@ -65,7 +65,10 @@ onMounted(() => {
/* Remove hardcoded colors and use theme variables */ /* Remove hardcoded colors and use theme variables */
background: var(--header-bg); background: var(--header-bg);
color: var(--text-color); color: var(--text-color);
padding: 1rem; height: var(--header-height);
padding: 0 1rem;
display: flex;
align-items: center;
/* box-shadow handled by glass-panel class */ /* box-shadow handled by glass-panel class */
position: sticky; position: sticky;
top: 0; top: 0;
@@ -77,7 +80,7 @@ onMounted(() => {
} }
.header-content { .header-content {
max-width: 100%; width: 100%;
padding: 0; padding: 0;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;

View File

@@ -16,7 +16,7 @@ const {
stopListening stopListening
} = useExtension() } = useExtension()
const { height: textareaHeight } = useFillHeight(textareaRef, 120) const { height: textareaHeight } = useFillHeight(textareaRef, 40)
// Watch for clipboard updates from extension // Watch for clipboard updates from extension
watch(lastClipboardText, (newText) => { watch(lastClipboardText, (newText) => {

View File

@@ -142,7 +142,6 @@ const generatePasswords = () => {
class="tool-textarea" class="tool-textarea"
v-model="result" v-model="result"
placeholder="Generated passwords will appear here..." placeholder="Generated passwords will appear here..."
readonly
></textarea> ></textarea>
</div> </div>
</div> </div>

View File

@@ -12,7 +12,7 @@ const format = useLocalStorage('format', 'png', 'qr-code')
const svgContent = ref('') const svgContent = ref('')
const previewRef = ref(null) const previewRef = ref(null)
const { height: previewHeight } = useFillHeight(previewRef, 40) // 40px margin bottom const { height: previewHeight } = useFillHeight(previewRef, 40) // 40px extra margin
const generateQR = async () => { const generateQR = async () => {
if (!text.value) { if (!text.value) {

View File

@@ -1,6 +1,7 @@
import { onMounted, onUnmounted, ref, nextTick } from 'vue' import { onMounted, onUnmounted, ref, nextTick } from 'vue'
import { UI_CONFIG } from '../config/ui'
export function useFillHeight(elementRef, marginBottom = 20) { export function useFillHeight(elementRef, extraMargin = 0) {
const height = ref('auto') const height = ref('auto')
const updateHeight = () => { const updateHeight = () => {
@@ -8,16 +9,10 @@ export function useFillHeight(elementRef, marginBottom = 20) {
const rect = elementRef.value.getBoundingClientRect() const rect = elementRef.value.getBoundingClientRect()
const windowHeight = window.innerHeight const windowHeight = window.innerHeight
// Calculate available space: window height - element top position - margin bottom
// We also need to account for the footer height if it's fixed or layout related
// The user mentioned "margin bottom from footer".
// If footer is in the flow, we might just want to fill the parent container?
// But user asked for JS resizing.
// Let's assume we want to fill down to (windowHeight - marginBottom). // Calculate available space: window height - element top position - footer height - padding - extra margin
// This assumes the element should stretch to the bottom of the viewport. const bottomOffset = UI_CONFIG.footerHeight + UI_CONFIG.pagePadding + extraMargin
const availableHeight = windowHeight - rect.top - bottomOffset
const availableHeight = windowHeight - rect.top - marginBottom
// Ensure minimum height // Ensure minimum height
if (availableHeight > 100) { if (availableHeight > 100) {

6
src/config/ui.js Normal file
View File

@@ -0,0 +1,6 @@
export const UI_CONFIG = {
headerHeight: 64,
footerHeight: 50,
pagePadding: 16 // Single side padding (1rem)
}