From 84815b79b523a41e168029cf065a867182d4e863 Mon Sep 17 00:00:00 2001 From: Vordgi Date: Mon, 2 Feb 2026 23:41:57 +0000 Subject: [PATCH 1/5] feat: implement font size picker --- app/assets/font-sizes.css | 167 +++++++++++++++++++++ app/components/Settings/TextSizePicker.vue | 38 +++++ app/composables/useColors.ts | 2 +- app/composables/useSettings.ts | 33 +++- app/pages/index.vue | 2 +- app/pages/settings.vue | 8 + app/utils/prehydrate.ts | 6 + i18n/locales/en.json | 10 +- lunaria/files/en-US.json | 10 +- nuxt.config.ts | 2 +- shared/utils/constants.ts | 8 + uno.config.ts | 66 ++++++++ 12 files changed, 345 insertions(+), 7 deletions(-) create mode 100644 app/assets/font-sizes.css create mode 100644 app/components/Settings/TextSizePicker.vue diff --git a/app/assets/font-sizes.css b/app/assets/font-sizes.css new file mode 100644 index 000000000..e4b24ebda --- /dev/null +++ b/app/assets/font-sizes.css @@ -0,0 +1,167 @@ +:root { + --text-9xl-font-size: 8rem; + --text-9xl-line-height: 1; + --text-8xl-font-size: 6rem; + --text-8xl-line-height: 1; + --text-7xl-font-size: 4.5rem; + --text-7xl-line-height: 1; + --text-6xl-font-size: 4rem; + --text-6xl-line-height: 1; + --text-5xl-font-size: 3rem; + --text-5xl-line-height: 1; + --text-4xl-font-size: 2.25rem; + --text-4xl-line-height: 2.5rem; + --text-3xl-font-size: 1.875rem; + --text-3xl-line-height: 2.25rem; + --text-2xl-font-size: 1.5rem; + --text-2xl-line-height: 2rem; + --text-xl-font-size: 1.25rem; + --text-xl-line-height: 1.75rem; + --text-lg-font-size: 1.125rem; + --text-lg-line-height: 1.75rem; + --text-base-font-size: 1rem; + --text-base-line-height: 1.5rem; + --text-sm-font-size: 0.875rem; + --text-sm-line-height: 1.25rem; + --text-xs-font-size: 0.75rem; + --text-xs-line-height: 1rem; + --text-2xs-font-size: 0.6875rem; + --text-2xs-line-height: 0.875rem; + --text-3xs-font-size: 0.625rem; + --text-3xs-line-height: 0.75rem; + --text-4xs-font-size: 0.5rem; + --text-4xs-line-height: 0.625rem; +} + +html[data-text-size='largest'] { + --text-7xl-font-size: 6rem; + --text-7xl-line-height: 1; + --text-6xl-font-size: 6rem; + --text-6xl-line-height: 1; + --text-5xl-font-size: 4.5rem; + --text-5xl-line-height: 1; + --text-4xl-font-size: 4rem; + --text-4xl-line-height: 1; + --text-3xl-font-size: 3rem; + --text-3xl-line-height: 1; + --text-2xl-font-size: 2.25rem; + --text-2xl-line-height: 2.5rem; + --text-xl-font-size: 1.875rem; + --text-xl-line-height: 2.25rem; + --text-lg-font-size: 1.5rem; + --text-lg-line-height: 2rem; + --text-base-font-size: 1.25rem; + --text-base-line-height: 1.75rem; + --text-sm-font-size: 1.125rem; + --text-sm-line-height: 1.75rem; + --text-xs-font-size: 1rem; + --text-xs-line-height: 1.5rem; + --text-2xs-font-size: 0.875rem; + --text-2xs-line-height: 1.25rem; + --text-3xs-font-size: 0.75rem; + --text-3xs-line-height: 1rem; + --text-4xs-font-size: 0.6875rem; + --text-4xs-line-height: 0.875rem; +} + +html[data-text-size='large'] { + --text-7xl-font-size: 6rem; + --text-7xl-line-height: 1; + --text-6xl-font-size: 4.5rem; + --text-6xl-line-height: 1; + --text-5xl-font-size: 4rem; + --text-5xl-line-height: 1; + --text-4xl-font-size: 3rem; + --text-4xl-line-height: 1; + --text-3xl-font-size: 2.25rem; + --text-3xl-line-height: 2.5rem; + --text-2xl-font-size: 1.875rem; + --text-2xl-line-height: 2.25rem; + --text-xl-font-size: 1.5rem; + --text-xl-line-height: 2rem; + --text-lg-font-size: 1.25rem; + --text-lg-line-height: 1.75rem; + --text-base-font-size: 1.125rem; + --text-base-line-height: 1.75rem; + --text-sm-font-size: 1rem; + --text-sm-line-height: 1.5rem; + --text-xs-font-size: 0.875rem; + --text-xs-line-height: 1.25rem; + --text-2xs-font-size: 0.75rem; + --text-2xs-line-height: 1rem; + --text-3xs-font-size: 0.6875rem; + --text-3xs-line-height: 0.875rem; + --text-4xs-font-size: 0.625rem; + --text-4xs-line-height: 0.75rem; +} + +html[data-text-size='small'] { + --text-9xl-font-size: 6rem; + --text-9xl-line-height: 1; + --text-8xl-font-size: 4.5rem; + --text-8xl-line-height: 1; + --text-7xl-font-size: 4rem; + --text-7xl-line-height: 1; + --text-6xl-font-size: 3rem; + --text-6xl-line-height: 1; + --text-5xl-font-size: 2.25rem; + --text-5xl-line-height: 1; + --text-4xl-font-size: 1.875rem; + --text-4xl-line-height: 2.25rem; + --text-3xl-font-size: 1.5rem; + --text-3xl-line-height: 2rem; + --text-2xl-font-size: 1.25rem; + --text-2xl-line-height: 1.75rem; + --text-xl-font-size: 1.125rem; + --text-xl-line-height: 1.75rem; + --text-lg-font-size: 1rem; + --text-lg-line-height: 1.5rem; + --text-base-font-size: 0.875rem; + --text-base-line-height: 1.25rem; + --text-sm-font-size: 0.75rem; + --text-sm-line-height: 1rem; + --text-xs-font-size: 0.6875rem; + --text-xs-line-height: 0.875rem; + --text-2xs-font-size: 0.625rem; + --text-2xs-line-height: 0.75rem; + --text-3xs-font-size: 0.5rem; + --text-3xs-line-height: 0.625rem; +} + +html[data-text-size='smallest'] { + --text-9xl-font-size: 4.5rem; + --text-9xl-line-height: 1; + --text-8xl-font-size: 4rem; + --text-8xl-line-height: 1; + --text-7xl-font-size: 3rem; + --text-7xl-line-height: 1; + --text-6xl-font-size: 2.25rem; + --text-6xl-line-height: 1; + --text-5xl-font-size: 1.875rem; + --text-5xl-line-height: 2.25rem; + --text-4xl-font-size: 1.5rem; + --text-4xl-line-height: 2rem; + --text-3xl-font-size: 1.25rem; + --text-3xl-line-height: 1.75rem; + --text-2xl-font-size: 1.125rem; + --text-2xl-line-height: 1.75rem; + --text-xl-font-size: 1rem; + --text-xl-line-height: 1.5rem; + --text-lg-font-size: 0.875rem; + --text-lg-line-height: 1.25rem; + --text-base-font-size: 0.75rem; + --text-base-line-height: 1rem; + --text-sm-font-size: 0.6875rem; + --text-sm-line-height: 0.875rem; + --text-xs-font-size: 0.625rem; + --text-xs-line-height: 0.75rem; + --text-2xs-font-size: 0.5625rem; + --text-2xs-line-height: 0.625rem; + --text-3xs-font-size: 0.5rem; + --text-3xs-line-height: 0.625rem; +} + +body { + font-size: var(--text-base-font-size); + line-height: var(--text-base-line-height); +} diff --git a/app/components/Settings/TextSizePicker.vue b/app/components/Settings/TextSizePicker.vue new file mode 100644 index 000000000..916525088 --- /dev/null +++ b/app/components/Settings/TextSizePicker.vue @@ -0,0 +1,38 @@ + + + diff --git a/app/composables/useColors.ts b/app/composables/useColors.ts index 3b66e25fd..7dd9ec738 100644 --- a/app/composables/useColors.ts +++ b/app/composables/useColors.ts @@ -81,7 +81,7 @@ export function useCssVariables( if (options.watchHtmlAttributes && isClientSupported.value) { useMutationObserver(document.documentElement, () => void colors.value, { attributes: true, - attributeFilter: ['class', 'style', 'data-theme', 'data-bg-theme'], + attributeFilter: ['class', 'style', 'data-theme', 'data-bg-theme', 'data-text-size'], }) } diff --git a/app/composables/useSettings.ts b/app/composables/useSettings.ts index b0c2f6ff6..2d2cec610 100644 --- a/app/composables/useSettings.ts +++ b/app/composables/useSettings.ts @@ -1,13 +1,14 @@ import type { RemovableRef } from '@vueuse/core' import { useLocalStorage } from '@vueuse/core' -import { ACCENT_COLORS } from '#shared/utils/constants' import type { LocaleObject } from '@nuxtjs/i18n' -import { BACKGROUND_THEMES } from '#shared/utils/constants' +import { ACCENT_COLORS, BACKGROUND_THEMES, TEXT_SIZES } from '#shared/utils/constants' type BackgroundThemeId = keyof typeof BACKGROUND_THEMES type AccentColorId = keyof typeof ACCENT_COLORS +type TextSizeId = keyof typeof TEXT_SIZES + /** * Application settings stored in localStorage */ @@ -20,6 +21,8 @@ export interface AppSettings { accentColorId: AccentColorId | null /** Preferred background shade */ preferredBackgroundTheme: BackgroundThemeId | null + /** Preferred text size */ + preferredTextSize: TextSizeId | null /** Hide platform-specific packages (e.g., @scope/pkg-linux-x64) from search results */ hidePlatformPackages: boolean /** User-selected locale */ @@ -36,6 +39,7 @@ const DEFAULT_SETTINGS: AppSettings = { hidePlatformPackages: true, selectedLocale: null, preferredBackgroundTheme: null, + preferredTextSize: null, sidebar: { collapsed: [], }, @@ -124,3 +128,28 @@ export function useBackgroundTheme() { setBackgroundTheme, } } + +export function useTextSize() { + const textSizes = Object.entries(TEXT_SIZES).map(([id, value]) => ({ + id: id as TextSizeId, + name: id, + value, + })) + + const { settings } = useSettings() + + function setTextSize(id: TextSizeId | null) { + if (id) { + document.documentElement.dataset.textSize = id + } else { + document.documentElement.removeAttribute('data-text-size') + } + settings.value.preferredTextSize = id + } + + return { + textSizes, + selectedTextSize: computed(() => settings.value.preferredTextSize), + setTextSize, + } +} diff --git a/app/pages/index.vue b/app/pages/index.vue index 9bf506981..4b347ab23 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -66,7 +66,7 @@ defineOgImageComponent('Default', {

{{ $t('tagline') }} diff --git a/app/pages/settings.vue b/app/pages/settings.vue index 38bba087b..52d1b9c41 100644 --- a/app/pages/settings.vue +++ b/app/pages/settings.vue @@ -105,6 +105,14 @@ const setLocale: typeof setNuxti18nLocale = locale => { + + +

+ + {{ $t('settings.text_size') }} + + +
diff --git a/app/utils/prehydrate.ts b/app/utils/prehydrate.ts index 562ef40b7..dd73461bb 100644 --- a/app/utils/prehydrate.ts +++ b/app/utils/prehydrate.ts @@ -41,6 +41,12 @@ export function initPreferencesOnPrehydrate() { document.documentElement.dataset.bgTheme = preferredBackgroundTheme } + // Apply text size + const preferredTextSize = settings.preferredTextSize + if (preferredTextSize) { + document.documentElement.dataset.textSize = preferredTextSize + } + // Read and apply package manager preference const storedPM = localStorage.getItem('npmx-pm') // Parse the stored value (it's stored as a JSON string by useLocalStorage) diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 29826daab..410f2e4b5 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -78,7 +78,15 @@ "accent_colors": "Accent colors", "clear_accent": "Clear accent color", "translation_progress": "Translation progress", - "background_themes": "Background shade" + "background_themes": "Background shade", + "text_size": "Text size", + "text_sizes": { + "largest": "Largest", + "large": "Larger", + "normal": "Normal", + "small": "Smaller", + "smallest": "Smallest" + } }, "i18n": { "missing_keys": "{count} missing translation | {count} missing translations", diff --git a/lunaria/files/en-US.json b/lunaria/files/en-US.json index 29826daab..410f2e4b5 100644 --- a/lunaria/files/en-US.json +++ b/lunaria/files/en-US.json @@ -78,7 +78,15 @@ "accent_colors": "Accent colors", "clear_accent": "Clear accent color", "translation_progress": "Translation progress", - "background_themes": "Background shade" + "background_themes": "Background shade", + "text_size": "Text size", + "text_sizes": { + "largest": "Largest", + "large": "Larger", + "normal": "Normal", + "small": "Smaller", + "smallest": "Smallest" + } }, "i18n": { "missing_keys": "{count} missing translation | {count} missing translations", diff --git a/nuxt.config.ts b/nuxt.config.ts index 8ba28e7b4..282f3066c 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -37,7 +37,7 @@ export default defineNuxtConfig({ storageKey: 'npmx-color-mode', }, - css: ['~/assets/main.css', 'vue-data-ui/style.css'], + css: ['~/assets/main.css', '~/assets/font-sizes.css', 'vue-data-ui/style.css'], $production: { debug: { diff --git a/shared/utils/constants.ts b/shared/utils/constants.ts index 582383508..f0930db98 100644 --- a/shared/utils/constants.ts +++ b/shared/utils/constants.ts @@ -47,3 +47,11 @@ export const BACKGROUND_THEMES = { slate: 'oklch(0.555 0.046 257.407)', black: 'oklch(0.4 0 0)', } as const + +export const TEXT_SIZES = { + smallest: 'smallest', + small: 'small', + normal: 'normal', + large: 'large', + largest: 'largest', +} as const diff --git a/uno.config.ts b/uno.config.ts index 3e3c38e92..5f448b662 100644 --- a/uno.config.ts +++ b/uno.config.ts @@ -39,6 +39,72 @@ export default defineConfig({ mono: "'Geist Mono', monospace", sans: "'Geist', system-ui, -apple-system, sans-serif", }, + text: { + '9xl': { + fontSize: 'var(--text-9xl-font-size)', + lineHeight: 'var(--text-9xl-line-height)', + }, + '8xl': { + fontSize: 'var(--text-8xl-font-size)', + lineHeight: 'var(--text-8xl-line-height)', + }, + '7xl': { + fontSize: 'var(--text-7xl-font-size)', + lineHeight: 'var(--text-7xl-line-height)', + }, + '6xl': { + fontSize: 'var(--text-6xl-font-size)', + lineHeight: 'var(--text-6xl-line-height)', + }, + '5xl': { + fontSize: 'var(--text-5xl-font-size)', + lineHeight: 'var(--text-5xl-line-height)', + }, + '4xl': { + fontSize: 'var(--text-4xl-font-size)', + lineHeight: 'var(--text-4xl-line-height)', + }, + '3xl': { + fontSize: 'var(--text-3xl-font-size)', + lineHeight: 'var(--text-3xl-line-height)', + }, + '2xl': { + fontSize: 'var(--text-2xl-font-size)', + lineHeight: 'var(--text-2xl-line-height)', + }, + 'xl': { + fontSize: 'var(--text-xl-font-size)', + lineHeight: 'var(--text-xl-line-height)', + }, + 'lg': { + fontSize: 'var(--text-lg-font-size)', + lineHeight: 'var(--text-lg-line-height)', + }, + 'base': { + fontSize: 'var(--text-base-font-size)', + lineHeight: 'var(--text-base-line-height)', + }, + 'sm': { + fontSize: 'var(--text-sm-font-size)', + lineHeight: 'var(--text-sm-line-height)', + }, + 'xs': { + fontSize: 'var(--text-xs-font-size)', + lineHeight: 'var(--text-xs-line-height)', + }, + '2xs': { + fontSize: 'var(--text-2xs-font-size)', + lineHeight: 'var(--text-2xs-line-height)', + }, + '3xs': { + fontSize: 'var(--text-3xs-font-size)', + lineHeight: 'var(--text-3xs-line-height)', + }, + '4xs': { + fontSize: 'var(--text-4xs-font-size)', + lineHeight: 'var(--text-4xs-line-height)', + }, + }, colors: { // Minimal black & white palette with subtle grays bg: { From e5a2df79bc6eb9bd204b286ee68947ad510b435a Mon Sep 17 00:00:00 2001 From: Vordgi Date: Mon, 2 Feb 2026 23:57:22 +0000 Subject: [PATCH 2/5] feat: use font-size variables instead of fixed values --- app/app.vue | 3 +- app/assets/main.css | 6 ++-- app/components/Code/Viewer.vue | 3 +- app/components/DependencyPathPopup.vue | 2 +- app/components/Header/AccountMenu.client.vue | 2 +- app/components/Package/Dependencies.vue | 2 +- app/components/Package/DownloadAnalytics.vue | 9 ++---- app/components/Package/Versions.vue | 22 +++++++------- app/components/Package/VulnerabilityTree.vue | 2 +- app/components/Readme.vue | 32 ++++++++++++++------ app/components/VersionSelector.vue | 2 +- app/components/compare/FacetSelector.vue | 10 +++--- app/pages/compare.vue | 6 ++-- 13 files changed, 57 insertions(+), 44 deletions(-) diff --git a/app/app.vue b/app/app.vue index 1e2fc1af2..07a71312d 100644 --- a/app/app.vue +++ b/app/app.vue @@ -139,7 +139,8 @@ if (import.meta.client) { padding: 0.5rem 1rem; background: var(--fg); color: var(--bg); - font-size: 0.875rem; + font-size: var(--text-sm-font-size); + line-height: var(--text-sm-line-height); z-index: 100; transition: top 0.2s ease; } diff --git a/app/assets/main.css b/app/assets/main.css index 85e8802a2..4b5f58772 100644 --- a/app/assets/main.css +++ b/app/assets/main.css @@ -270,7 +270,8 @@ html.light .shiki span { p > span > code, .line-clamp-2 code { @apply font-mono; - font-size: 0.85em; + font-size: var(--text-sm-font-size); + line-height: var(--text-sm-line-height); background: var(--bg-muted); padding: 0.1em 0.3em; border-radius: 3px; @@ -292,7 +293,8 @@ input[type='search'] { input, select, textarea { - font-size: 16px !important; + font-size: var(--text-base-font-size) !important; + line-height: var(--text-base-line-height) !important; } } diff --git a/app/components/Code/Viewer.vue b/app/components/Code/Viewer.vue index fb6b2a571..b696d5a8d 100644 --- a/app/components/Code/Viewer.vue +++ b/app/components/Code/Viewer.vue @@ -90,7 +90,8 @@ watch(