diff --git a/pages/admin/system-info/index(default).phtml b/pages/admin/system-info/index(default).phtml index 218407f..5f7e761 100644 --- a/pages/admin/system-info/index(default).phtml +++ b/pages/admin/system-info/index(default).phtml @@ -17,7 +17,6 @@ $healthChecks = $healthChecks ?? [];

-
diff --git a/tests/Architecture/TypographyTokenContractTest.php b/tests/Architecture/TypographyTokenContractTest.php new file mode 100644 index 0000000..8fe425c --- /dev/null +++ b/tests/Architecture/TypographyTokenContractTest.php @@ -0,0 +1,214 @@ + + */ + private const EXCLUDED_PATHS = [ + 'web/css/vendor/', + 'web/css/vendor-overrides/', + 'web/css/base/variables.base.css', + ]; + + /** + * Patterns that identify intentional exceptions (icon fonts, em-relative badges, etc.). + * + * @var list + */ + private const EXCEPTION_MARKERS = [ + 'icon-font metric', + 'em-relative sizing', + 'intentional px', + ]; + + // ────────────────────────────────────────────────────────────── + // font-size + // ────────────────────────────────────────────────────────────── + + public function testFontSizeUsesTokens(): void + { + $violations = $this->scanForRawValues( + '/font-size\s*:\s*+(?!var\(--text-|var\(--app-|inherit|unset|initial|revert|small\b|0[;\s])/', + self::EXCEPTION_MARKERS, + ); + + $this->assertSame( + [], + $violations, + "Raw font-size values found instead of --text-* tokens:\n" . implode("\n", $violations), + ); + } + + // ────────────────────────────────────────────────────────────── + // line-height + // ────────────────────────────────────────────────────────────── + + public function testLineHeightUsesTokens(): void + { + $violations = $this->scanForRawValues( + '/(?assertSame( + [], + $violations, + "Raw line-height values found instead of --leading-* tokens:\n" . implode("\n", $violations), + ); + } + + // ────────────────────────────────────────────────────────────── + // font-weight + // ────────────────────────────────────────────────────────────── + + public function testFontWeightUsesTokens(): void + { + $violations = $this->scanForRawValues( + '/font-weight\s*:\s*+(?!var\(--font-|var\(--app-font-weight\)|var\(--app-form-label-font-weight|inherit|unset|initial|revert|normal|bold\b|bolder|lighter)/', + self::EXCEPTION_MARKERS, + ); + + $this->assertSame( + [], + $violations, + "Raw font-weight values found instead of --font-* tokens:\n" . implode("\n", $violations), + ); + } + + // ────────────────────────────────────────────────────────────── + // Token definitions exist + // ────────────────────────────────────────────────────────────── + + public function testTypographyTokensAreDefined(): void + { + $content = $this->readProjectFile('web/css/base/variables.base.css'); + + $requiredTokens = [ + '--text-2xs', '--text-xs', '--text-sm', '--text-base', '--text-lg', + '--text-xl', '--text-2xl', '--text-3xl', '--text-4xl', '--text-5xl', + '--leading-none', '--leading-tight', '--leading-snug', '--leading-normal', + '--leading-relaxed', '--leading-loose', + '--font-regular', '--font-medium', '--font-semibold', '--font-bold', + '--tracking-tight', '--tracking-normal', '--tracking-wide', + ]; + + foreach ($requiredTokens as $token) { + $this->assertStringContainsString( + $token . ':', + $content, + "Typography token {$token} is missing from variables.base.css", + ); + } + } + + public function testHeadingTokensUseClamp(): void + { + $content = $this->readProjectFile('web/css/base/variables.base.css'); + + $clampTokens = ['--text-2xl', '--text-3xl', '--text-4xl', '--text-5xl']; + + foreach ($clampTokens as $token) { + $this->assertMatchesRegularExpression( + '/' . preg_quote($token, '/') . ':\s*clamp\(/', + $content, + "Heading token {$token} must use clamp() for responsive scaling", + ); + } + } + + // ────────────────────────────────────────────────────────────── + // Helpers + // ────────────────────────────────────────────────────────────── + + /** + * Scan all CSS files in web/css/ for lines matching the violation pattern, + * excluding allowed paths and lines that contain an exception marker. + * + * @param string $violationPattern Regex that matches a raw value + * @param list $exceptionMarkers Strings whose presence on the same line excuses the match + * @return list Violation descriptions ("file:line: content") + */ + private function scanForRawValues(string $violationPattern, array $exceptionMarkers): array + { + $root = $this->projectRootPath(); + $cssDir = $root . '/web/css'; + $this->assertDirectoryExists($cssDir); + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($cssDir), + ); + + $violations = []; + + /** @var \SplFileInfo $file */ + foreach ($iterator as $file) { + if (!$file->isFile() || $file->getExtension() !== 'css') { + continue; + } + + $relativePath = str_replace($root . '/', '', $file->getPathname()); + + // Skip excluded paths + foreach (self::EXCLUDED_PATHS as $excluded) { + if (str_starts_with($relativePath, $excluded)) { + continue 2; + } + } + + $content = file_get_contents($file->getPathname()); + $this->assertNotFalse($content, 'Could not read file: ' . $relativePath); + + $lines = explode("\n", $content); + foreach ($lines as $lineNumber => $line) { + if (!preg_match($violationPattern, $line)) { + continue; + } + + // Check if the line contains a documented exception marker + $isException = false; + foreach ($exceptionMarkers as $marker) { + if (str_contains($line, $marker)) { + $isException = true; + break; + } + } + + if ($isException) { + continue; + } + + // Skip CSS custom property definitions (--app-font-size, --app-line-height, etc.) + if (preg_match('/^\s*--app-/', $line)) { + continue; + } + + $violations[] = sprintf('%s:%d: %s', $relativePath, $lineNumber + 1, trim($line)); + } + } + + sort($violations); + return $violations; + } +} diff --git a/web/css/base/variables.base.css b/web/css/base/variables.base.css index bcf6b60..eaee950 100644 --- a/web/css/base/variables.base.css +++ b/web/css/base/variables.base.css @@ -74,6 +74,37 @@ --app-empty-bg: var(--app-card-background-color); --app-empty-text: var(--app-color); --app-empty-hint: var(--app-muted-color); + + /* ── Typography scale (major-third 1.25 ratio) ──────────────── */ + --text-2xs: 0.625rem; + --text-xs: 0.75rem; + --text-sm: 0.875rem; + --text-base: 1rem; + --text-lg: 1.125rem; + --text-xl: 1.25rem; + --text-2xl: clamp(1.25rem, 1.1rem + 0.5vw, 1.5rem); + --text-3xl: clamp(1.5rem, 1.3rem + 0.75vw, 1.875rem); + --text-4xl: clamp(1.75rem, 1.5rem + 1vw, 2.25rem); + --text-5xl: clamp(2.25rem, 1.9rem + 1.5vw, 3rem); + + /* ── Line-height tokens ─────────────────────────────────────── */ + --leading-none: 1; + --leading-tight: 1.2; + --leading-snug: 1.375; + --leading-normal: 1.5; + --leading-relaxed: 1.625; + --leading-loose: 1.75; + + /* ── Font-weight tokens ─────────────────────────────────────── */ + --font-regular: 400; + --font-medium: 500; + --font-semibold: 600; + --font-bold: 700; + + /* ── Letter-spacing tokens ──────────────────────────────────── */ + --tracking-tight: -0.01em; + --tracking-normal: 0; + --tracking-wide: 0.025em; } /* Icons */ diff --git a/web/css/components/app-active-filter-chips.css b/web/css/components/app-active-filter-chips.css index 23e938f..be060b2 100644 --- a/web/css/components/app-active-filter-chips.css +++ b/web/css/components/app-active-filter-chips.css @@ -38,9 +38,9 @@ align-items: center; gap: 0.45rem; padding: 0.3rem 0.65rem; - font-size: 0.78rem; - line-height: 1.15; - font-weight: 500; + font-size: var(--text-xs); + line-height: var(--leading-tight); + font-weight: var(--font-medium); color: var(--app-form-element-color); --app-background-color: var(--app-form-element-background-color); --app-border-color: var(--app-form-element-border-color); @@ -76,7 +76,7 @@ width: 1rem; height: 1rem; border-radius: 999px; - font-size: 0.58rem; + font-size: var(--text-2xs); background: var(--app-muted-border-color); } diff --git a/web/css/components/app-badges.css b/web/css/components/app-badges.css index 68cd4a5..df52e4f 100644 --- a/web/css/components/app-badges.css +++ b/web/css/components/app-badges.css @@ -7,9 +7,9 @@ gap: 0.35em; padding: 0.15em 0.6em; border-radius: 999px; - font-size: 0.85em; - font-weight: 400; - line-height: 1.2; + font-size: 0.85em; /* em-relative sizing — intentional, scales with parent */ + font-weight: var(--font-regular); + line-height: var(--leading-tight); border: 1px solid transparent; white-space: nowrap; } @@ -26,12 +26,12 @@ align-items: center; justify-content: center; color: inherit; - line-height: 1; - font-size: 10px; + line-height: var(--leading-none); + font-size: 10px; /* icon-font metric — intentional px */ } .badge .badge-copy-icon .bi { - font-size: 0.95em; + font-size: 0.95em; /* em-relative sizing — intentional, scales with parent */ } .badge .badge-copy-icon .bi-check2 { diff --git a/web/css/components/app-brand.css b/web/css/components/app-brand.css index b7a7870..7a8508d 100644 --- a/web/css/components/app-brand.css +++ b/web/css/components/app-brand.css @@ -14,17 +14,17 @@ img.brand-logo { } .brand-name { - line-height: 1; - font-weight: bold; + line-height: var(--leading-none); + font-weight: var(--font-bold); color: var(--app-contrast); } .brand-info { - line-height: 1; + line-height: var(--leading-none); } .brand-slogan { - font-size: 0.85em; + font-size: 0.85em; /* em-relative sizing — intentional, scales with parent */ color: var(--app-muted-color); } diff --git a/web/css/components/app-breadcrumb.css b/web/css/components/app-breadcrumb.css index 3961740..ec78cc5 100644 --- a/web/css/components/app-breadcrumb.css +++ b/web/css/components/app-breadcrumb.css @@ -5,7 +5,7 @@ display: flex; align-items: center; gap: 3px; - font-size: 11px; + font-size: var(--text-xs); } .app-breadcrumb a { diff --git a/web/css/components/app-dashboard-titlebar.css b/web/css/components/app-dashboard-titlebar.css index 66a4539..3aa160d 100644 --- a/web/css/components/app-dashboard-titlebar.css +++ b/web/css/components/app-dashboard-titlebar.css @@ -22,7 +22,7 @@ .app-dashboard-titlebar [role="button"] { --app-button-padding-horizontal: 10px; --app-button-padding-vertical: 6px; - font-size: 13px; + font-size: var(--text-sm); margin: 0; } @@ -37,7 +37,7 @@ background: var(--app-muted-border-color); width: 35px; height: 29px; - font-size: 15px; + font-size: 15px; /* icon-font metric — intentional px */ border-radius: var(--app-border-radius); display: inline-flex; align-items: center; @@ -49,7 +49,7 @@ display: flex; align-items: center; gap: 7px; - line-height: 1; + line-height: var(--leading-none); } .app-dashboard-titlebar h1 a:hover i { diff --git a/web/css/components/app-details-card.css b/web/css/components/app-details-card.css index c38265d..1f4a369 100644 --- a/web/css/components/app-details-card.css +++ b/web/css/components/app-details-card.css @@ -12,8 +12,8 @@ details.app-details-card > summary { margin: 0; padding: calc(var(--app-spacing) * 0.75) var(--app-spacing); - font-size: 0.85rem; - line-height: 1.25; + font-size: var(--text-sm); + line-height: var(--leading-tight); text-transform: none; background: var(--app-card-sectioning-background-color); display: flex; diff --git a/web/css/components/app-details-titlebar.css b/web/css/components/app-details-titlebar.css index f97b73f..c78f154 100644 --- a/web/css/components/app-details-titlebar.css +++ b/web/css/components/app-details-titlebar.css @@ -22,13 +22,13 @@ .app-details-titlebar [role="button"] { --app-button-padding-horizontal: 10px; --app-button-padding-vertical: 6px; - font-size: 13px; + font-size: var(--text-sm); margin: 0; } .app-details-titlebar button i, .app-details-titlebar [role="button"] i { - font-size: 10px; + font-size: 10px; /* icon-font metric — intentional px */ } .app-details-titlebar-actions { @@ -47,7 +47,7 @@ background: var(--app-background-color); width: 35px; height: 29px; - font-size: 15px; + font-size: 15px; /* icon-font metric — intentional px */ border-radius: var(--app-border-radius); display: inline-flex; align-items: center; @@ -62,7 +62,7 @@ display: flex; align-items: center; gap: 7px; - line-height: 1; + line-height: var(--leading-none); } .app-details-titlebar details.dropdown { margin-bottom: 0; diff --git a/web/css/components/app-details-validation-summary.css b/web/css/components/app-details-validation-summary.css index 64d0a55..5fe6cc7 100644 --- a/web/css/components/app-details-validation-summary.css +++ b/web/css/components/app-details-validation-summary.css @@ -27,7 +27,7 @@ .app-details-validation-summary-title { margin: 0; - font-weight: 600; + font-weight: var(--font-semibold); color: var(--app-validation-summary-title-color); } diff --git a/web/css/components/app-details.css b/web/css/components/app-details.css index 0a2d61e..d20628b 100644 --- a/web/css/components/app-details.css +++ b/web/css/components/app-details.css @@ -44,13 +44,13 @@ display: inline-flex; align-items: center; justify-content: center; - font-weight: 600; + font-weight: var(--font-semibold); color: var(--app-muted-color); } .user-avatar-placeholder, .entity-avatar-placeholder { - font-size: 1.5rem; + font-size: var(--text-2xl); } .user-avatar-image { diff --git a/web/css/components/app-empty-state.css b/web/css/components/app-empty-state.css index 9d95219..cd14dc8 100644 --- a/web/css/components/app-empty-state.css +++ b/web/css/components/app-empty-state.css @@ -41,15 +41,15 @@ .app-empty-state-message { margin: 0; color: var(--app-empty-hint); - font-size: 12px; - line-height: 1.4; + font-size: var(--text-xs); + line-height: var(--leading-snug); } .app-empty-state-hint { margin: 0; color: var(--app-empty-hint); - font-size: 0.85rem; - line-height: 1.3; + font-size: var(--text-sm); + line-height: var(--leading-snug); opacity: 0.75; } diff --git a/web/css/components/app-filter-drawer.css b/web/css/components/app-filter-drawer.css index 145fa86..2aec688 100644 --- a/web/css/components/app-filter-drawer.css +++ b/web/css/components/app-filter-drawer.css @@ -55,8 +55,8 @@ .app-filter-drawer-header h2 { margin: 0; - font-size: 0.8125rem; - line-height: 1.2; + font-size: var(--text-sm); + line-height: var(--leading-tight); } .app-filter-drawer-header .icon-button { @@ -111,7 +111,7 @@ .app-filter-drawer-body .app-list-toolbar .app-field > span, .app-filter-drawer-body .app-list-toolbar > label > span { margin: 0 !important; - line-height: 1.2; + line-height: var(--leading-tight); } .app-filter-drawer-body .app-list-toolbar input:not([type="hidden"]), @@ -153,7 +153,7 @@ .app-list-toolbar .multi-select .multi-select-header-option { - line-height: 1.2; + line-height: var(--leading-tight); } .app-filter-drawer-body #address-book-custom-filters { diff --git a/web/css/components/app-flash.css b/web/css/components/app-flash.css index d2341b8..8db0981 100644 --- a/web/css/components/app-flash.css +++ b/web/css/components/app-flash.css @@ -60,8 +60,8 @@ position: relative; overflow: hidden; color: var(--app-notice-border-color); - font-size: 0.875rem; - line-height: 1.4; + font-size: var(--text-sm); + line-height: var(--leading-snug); pointer-events: auto; margin: 0; animation: toast-slide-in 250ms ease-out; @@ -159,7 +159,7 @@ opacity: 0.6; cursor: pointer; padding: 2px 4px; - line-height: 1; + line-height: var(--leading-none); flex-shrink: 0; } diff --git a/web/css/components/app-form-info-tile.css b/web/css/components/app-form-info-tile.css index 6a3dce2..5631d10 100644 --- a/web/css/components/app-form-info-tile.css +++ b/web/css/components/app-form-info-tile.css @@ -28,13 +28,13 @@ .app-form-info-tile-label { display: block; color: var(--app-muted-color); - line-height: 1.3; + line-height: var(--leading-snug); } .app-form-info-tile-value { display: block; color: var(--app-contrast); - line-height: 1.35; + line-height: var(--leading-snug); } .app-form-info-tile--success { diff --git a/web/css/components/app-forms.css b/web/css/components/app-forms.css index 4e343c0..78be6d2 100644 --- a/web/css/components/app-forms.css +++ b/web/css/components/app-forms.css @@ -1,7 +1,7 @@ @layer components { /* Form element enhancements — validation hints, color inputs, checkbox/radio labels, disabled states. */ .form-hint { - font-size: small; + font-size: var(--text-sm); } ul.form-hint-list { @@ -25,19 +25,19 @@ content: "\F26B"; font-family: "Bootstrap-icons"; margin-left: 4px; - font-size: 10px; + font-size: 10px; /* icon-font metric — intentional px */ } ul.form-hint-list li.is-invalid:after { content: "\F33A"; font-family: "Bootstrap-icons"; margin-left: 4px; - font-size: 10px; + font-size: 10px; /* icon-font metric — intentional px */ } label:has([type="checkbox"], [type="radio"]) { display: flex; - line-height: 1; + line-height: var(--leading-none); align-items: center; } diff --git a/web/css/components/app-list-tabs.css b/web/css/components/app-list-tabs.css index f11f357..5e7e1ba 100644 --- a/web/css/components/app-list-tabs.css +++ b/web/css/components/app-list-tabs.css @@ -14,7 +14,7 @@ padding: 5px 10px 5px 10px; color: var(--list-tabs-muted); cursor: pointer; - font-weight: 500; + font-weight: var(--font-medium); border-bottom: 2px solid transparent; text-decoration: none; } diff --git a/web/css/components/app-list-titlebar.css b/web/css/components/app-list-titlebar.css index 658c29c..a21a5a0 100644 --- a/web/css/components/app-list-titlebar.css +++ b/web/css/components/app-list-titlebar.css @@ -22,7 +22,7 @@ .app-list-titlebar [role="button"] { --app-button-padding-horizontal: 10px; --app-button-padding-vertical: 6px; - font-size: 13px; + font-size: var(--text-sm); margin: 0; } @@ -37,7 +37,7 @@ background: var(--app-muted-border-color); width: 35px; height: 35px; - font-size: 16px; + font-size: 16px; /* icon-font metric — intentional px */ border-radius: var(--app-border-radius); display: inline-flex; align-items: center; diff --git a/web/css/components/app-list-toolbar.css b/web/css/components/app-list-toolbar.css index 74ff3e2..92ab648 100644 --- a/web/css/components/app-list-toolbar.css +++ b/web/css/components/app-list-toolbar.css @@ -13,7 +13,7 @@ width: auto !important; --app-form-element-spacing-horizontal: 10px; --app-form-element-spacing-vertical: 6px; - font-size: 13px !important; + font-size: var(--text-sm) !important; height: auto !important; } .app-list-toolbar > * > * { @@ -21,7 +21,7 @@ width: auto !important; --app-form-element-spacing-horizontal: 10px; --app-form-element-spacing-vertical: 6px; - font-size: 13px !important; + font-size: var(--text-sm) !important; height: auto !important; } .app-list-toolbar > * > * > * { @@ -29,7 +29,7 @@ width: auto !important; --app-form-element-spacing-horizontal: 10px; --app-form-element-spacing-vertical: 6px; - font-size: 13px !important; + font-size: var(--text-sm) !important; height: auto !important; } @@ -55,7 +55,7 @@ .app-list-toolbar > label > span { display: block; - font-size: 11px !important; + font-size: var(--text-xs) !important; } .app-list-toolbar .app-grid-page-size select { @@ -76,7 +76,7 @@ .multi-select .multi-select-header .multi-select-header-placeholder { - font-size: 13px; + font-size: var(--text-sm); } .app-list-toolbar .multi-select .multi-select-header, @@ -95,6 +95,6 @@ border: 0 !important; padding: 0 !important; padding-left: 3px !important; - font-size: 11px !important; + font-size: var(--text-xs) !important; } } diff --git a/web/css/components/app-search.css b/web/css/components/app-search.css index c60e26e..9f0ed4a 100644 --- a/web/css/components/app-search.css +++ b/web/css/components/app-search.css @@ -15,8 +15,8 @@ border-radius: 6px; background: var(--app-card-background-color); color: var(--app-muted-color); - font-size: 0.75rem; - line-height: 1; + font-size: var(--text-xs); + line-height: var(--leading-none); padding: 0.2rem 0.4rem; pointer-events: none; } @@ -28,13 +28,13 @@ border-radius: var(--app-border-radius); text-align: center; color: var(--app-muted-color); - font-size: 0.9rem; + font-size: var(--text-sm); cursor: text; } .app-search-empty-state .bi-search { display: inline-block; - font-size: 1.25rem; + font-size: var(--text-xl); margin-bottom: 0.5rem; color: var(--app-color); } @@ -57,7 +57,7 @@ ul.app-search-results > li > a { } ul.app-search-results .badge { - font-size: 10px; + font-size: 10px; /* icon-font metric — intentional px */ padding: 1px; min-width: 18px; text-align: center; @@ -73,7 +73,7 @@ ul.app-search-results .badge { ul.app-search-preview { position: relative; - font-size: 11px; + font-size: var(--text-xs); padding: 0 0 0 0; margin: 0; opacity: 0.75; @@ -120,7 +120,7 @@ input#side-search { --app-form-element-spacing-horizontal: 10px; --app-form-element-spacing-vertical: 6px; height: auto; - font-size: 13px; + font-size: var(--text-sm); padding-right: 3rem; margin-bottom:0; } @@ -160,18 +160,18 @@ ul.app-search-preview:has(li) { .search-result-title { - font-weight: 600; + font-weight: var(--font-semibold); text-decoration: none; } .search-result-desc { margin: 0; color: var(--app-muted-color); - font-size: small; + font-size: var(--text-sm); } .search-result-type { color: var(--app-muted-color); - font-weight: 600; + font-weight: var(--font-semibold); } } diff --git a/web/css/components/app-tabs.css b/web/css/components/app-tabs.css index 8eded2e..95bf390 100644 --- a/web/css/components/app-tabs.css +++ b/web/css/components/app-tabs.css @@ -40,8 +40,8 @@ border-radius: var(--app-border-radius); background-color: transparent; color: var(--app-secondary); - font-size: 13px; - line-height: 1; + font-size: var(--text-sm); + line-height: var(--leading-none); cursor: pointer; z-index: 1; margin-block: auto; diff --git a/web/css/components/app-tile.css b/web/css/components/app-tile.css index 185a0d5..64c66f2 100644 --- a/web/css/components/app-tile.css +++ b/web/css/components/app-tile.css @@ -67,14 +67,14 @@ border-radius: 10px; background: var(--tile-icon-bg); color: var(--tile-icon-color); - font-size: 1.1rem; + font-size: var(--text-lg); } .app-tile-count { position: absolute; top: calc(var(--app-spacing) * 1); right: calc(var(--app-spacing) * 1); - font-weight: 700; + font-weight: var(--font-bold); color: var(--tile-count-color); } @@ -93,7 +93,7 @@ right: calc(var(--app-spacing) * 1); bottom: calc(var(--app-spacing) * 1); color: var(--tile-link-color); - font-size: 0.9rem; + font-size: var(--text-sm); } .app-tile.is-small { @@ -106,7 +106,7 @@ width: 25px; height: 25px; border-radius: 6px; - font-size: 12px; + font-size: 12px; /* icon-font metric — intentional px */ margin-right: 10px; } @@ -121,14 +121,14 @@ } .app-tile.is-small .app-tile-label { margin-top: 0; - font-weight: 400; + font-weight: var(--font-regular); } .app-stats-table-header { border-bottom: 1px solid var(--app-border); padding: 4px 10px 4px 10px; text-transform: uppercase; - font-size: 13px; + font-size: var(--text-sm); letter-spacing: 0.4px; background: var(--app-blockquote-background-color); } diff --git a/web/css/components/app-tooltips.css b/web/css/components/app-tooltips.css index 192bee9..1ce5ecc 100644 --- a/web/css/components/app-tooltips.css +++ b/web/css/components/app-tooltips.css @@ -22,7 +22,7 @@ border: 1px solid var(--tooltip-border); border-radius: var(--tooltip-radius); box-shadow: var(--tooltip-shadow); - font-size: 0.75rem; + font-size: var(--text-xs); padding: 0.35rem 0.6rem; white-space: nowrap; } diff --git a/web/css/layout/app-shell.css b/web/css/layout/app-shell.css index ad19053..9a7ba77 100644 --- a/web/css/layout/app-shell.css +++ b/web/css/layout/app-shell.css @@ -32,42 +32,42 @@ h4, h5, h6 { - --app-font-weight: 700; + --app-font-weight: var(--font-bold); } h1 { - --app-font-size: 1.5rem; - --app-line-height: 1.125; + --app-font-size: var(--text-2xl); + --app-line-height: var(--leading-tight); --app-typography-spacing-top: 3rem; } h2 { - --app-font-size: 1.4rem; - --app-line-height: 1.15; + --app-font-size: var(--text-xl); + --app-line-height: var(--leading-tight); --app-typography-spacing-top: 2.625rem; } h3 { - --app-font-size: 1.3rem; - --app-line-height: 1.175; + --app-font-size: var(--text-lg); + --app-line-height: var(--leading-tight); --app-typography-spacing-top: 2.25rem; } h4 { - --app-font-size: 1.2rem; - --app-line-height: 1.2; + --app-font-size: var(--text-base); + --app-line-height: var(--leading-tight); --app-typography-spacing-top: 1.874rem; } h5 { - --app-font-size: 1.1rem; - --app-line-height: 1.225; + --app-font-size: var(--text-base); + --app-line-height: var(--leading-tight); --app-typography-spacing-top: 1.6875rem; } h6 { - --app-font-size: 1rem; - --app-line-height: 1.25; + --app-font-size: var(--text-sm); + --app-line-height: var(--leading-tight); --app-typography-spacing-top: 1.5rem; } @@ -75,7 +75,7 @@ tfoot th, thead td, thead th { - --app-font-weight: 600; + --app-font-weight: var(--font-semibold); --app-border-width: 2px; } @@ -177,7 +177,7 @@ th { text-transform: uppercase; - font-size: 11px; + font-size: var(--text-xs); letter-spacing: 0.4px; border-top: 1px solid var(--app-border); } @@ -562,8 +562,8 @@ sub, sup { position: relative; - font-size: 0.75em; - line-height: 0; + font-size: 0.75em; /* em-relative sizing — intentional, scales with parent */ + line-height: 0; /* intentional px — sub/sup vertical alignment */ vertical-align: baseline; } @@ -654,7 +654,7 @@ hgroup > :not(:first-child):last-child { --app-color: var(--app-muted-color); --app-font-weight: unset; - font-size: 1rem; + font-size: var(--text-base); } :where(ol, ul) li { @@ -848,7 +848,7 @@ box-shadow: var(--app-box-shadow); color: var(--app-color); font-weight: var(--app-font-weight); - font-size: 1rem; + font-size: var(--text-base); line-height: var(--app-line-height); text-align: center; text-decoration: none; @@ -872,7 +872,7 @@ ).small { --app-button-padding-vertical: var(--app-button-padding-vertical-small); --app-button-padding-horizontal: var(--app-button-padding-horizontal-small); - font-size: 13px; + font-size: var(--text-sm); } [role="button"]:is(:hover, :active, :focus), @@ -1170,7 +1170,7 @@ kbd, pre, samp { - font-size: 0.875em; + font-size: 0.875em; /* em-relative sizing — intentional, scales with parent */ font-family: var(--app-font-family); } @@ -1240,7 +1240,7 @@ --app-box-shadow: none; --app-button-padding-vertical: var(--app-button-padding-vertical-small); --app-button-padding-horizontal: var(--app-button-padding-horizontal-small); - font-size: 0.75rem; + font-size: var(--text-xs); } .code-toolbar > .toolbar .copy-to-clipboard-button:is(:hover, :focus) { @@ -1291,7 +1291,7 @@ select, textarea { margin: 0; - font-size: 1rem; + font-size: var(--text-base); line-height: var(--app-line-height); font-family: inherit; letter-spacing: inherit; @@ -1780,7 +1780,7 @@ border: var(--app-border-width) solid var(--app-border-color); border-radius: 1.25em; background-color: var(--app-background-color); - line-height: 1.25em; + line-height: 1.25em; /* em-relative sizing — intentional, switch control layout */ } [type="checkbox"][role="switch"]:not([aria-invalid]) { @@ -2159,7 +2159,7 @@ } details summary { - line-height: 1rem; + line-height: var(--leading-none); list-style-type: none; cursor: pointer; transition: color var(--app-transition); @@ -2937,7 +2937,7 @@ --app-nav-link-spacing-horizontal: 0.25rem; align-items: center; justify-content: start; - font-size: 0.875em; + font-size: 0.875em; /* em-relative sizing — intentional, scales with parent */ } nav[aria-label="breadcrumb"] ol, diff --git a/web/css/layout/app-sidebar.css b/web/css/layout/app-sidebar.css index 4c8e6c4..c41334f 100644 --- a/web/css/layout/app-sidebar.css +++ b/web/css/layout/app-sidebar.css @@ -3,9 +3,9 @@ background: var(--app-sidebar-background-color); --app-sidebar-gap: 0.5rem; --app-sidebar-item-gap: 10px; - --app-sidebar-title-size: 10px; - --app-sidebar-summary-size: 12px; - --app-sidebar-tenant-size: 14px; + --app-sidebar-title-size: 10px; /* icon-font metric — intentional px */ + --app-sidebar-summary-size: var(--text-xs); + --app-sidebar-tenant-size: var(--text-sm); --app-sidebar-titlebar-height: 30px; --app-sidebar-border-width: 2px; --app-sidebar-summary-max-width: 142px; @@ -37,7 +37,7 @@ } .app-sidebar-group.app-sidebar-admin-group details summary i { - font-size: 10px; + font-size: 10px; /* icon-font metric — intentional px */ position: relative; top: -4px; margin-right: 3px; @@ -100,8 +100,8 @@ } .app-sidebar-tenant-name { - font-weight: 600; - font-size: 17px; + font-weight: var(--font-semibold); + font-size: var(--text-base); color: var(--app-contrast); text-align: left; overflow: hidden; @@ -126,7 +126,7 @@ background: transparent; color: var(--app-muted-color); padding: 0.25rem; - font-size: 0.9rem; + font-size: var(--text-sm); cursor: pointer; margin-bottom: 0; } diff --git a/web/css/layout/app-topbar.css b/web/css/layout/app-topbar.css index d0f05f9..acaacc4 100644 --- a/web/css/layout/app-topbar.css +++ b/web/css/layout/app-topbar.css @@ -129,8 +129,8 @@ main:has(#app-details-aside-section) .app-topbar-menu-item-detail-sidebar { .app-topbar-tenant-name { display: inline-block; max-width: min(20rem, 38vw); - font-size: 0.86rem; - line-height: 1.2; + font-size: var(--text-sm); + line-height: var(--leading-tight); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -224,15 +224,15 @@ main:has(#app-details-aside-section) .app-topbar-menu-item-detail-sidebar { .app-topbar-hotkey { color: var(--app-muted-color); - font-size: 0.82em; + font-size: var(--text-xs); white-space: nowrap; } .app-topbar-menu-list li.app-topbar-menu-heading { padding: calc(var(--app-spacing) * 0.5) 0.5rem calc(var(--app-spacing) * 0.35); color: var(--app-muted-color); - font-weight: 500; - letter-spacing: 0.03em; + font-weight: var(--font-medium); + letter-spacing: var(--tracking-wide); text-transform: uppercase; pointer-events: none; } diff --git a/web/css/pages/app-docs.css b/web/css/pages/app-docs.css index b70a185..20e2ad2 100644 --- a/web/css/pages/app-docs.css +++ b/web/css/pages/app-docs.css @@ -83,7 +83,7 @@ } .app-docs-nav-group summary small { - font-size: 0.7rem; + font-size: var(--text-xs); letter-spacing: 0.05em; text-transform: uppercase; display: inline; @@ -107,7 +107,7 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - font-size: 0.875rem; + font-size: var(--text-sm); border-radius: 0; text-decoration: none; } @@ -120,7 +120,7 @@ .app-docs-nav a.active { color: var(--app-primary); border-left-color: var(--app-primary); - font-weight: 500; + font-weight: var(--font-medium); } /* ─── TOC ─────────────────────────────────────────────────────── */ @@ -132,7 +132,7 @@ .app-docs-toc > small { display: block; padding: 0 0 0.5rem; - font-size: 0.7rem; + font-size: var(--text-xs); letter-spacing: 0.05em; text-transform: uppercase; } @@ -151,7 +151,7 @@ display: block; text-decoration: none; color: inherit; - line-height: 1.35; + line-height: var(--leading-snug); padding-block: 0.12rem; } @@ -171,7 +171,7 @@ } .app-docs-toc-level-3 a { - font-size: 0.75rem; + font-size: var(--text-xs); padding-left: 0.45rem; } @@ -208,41 +208,41 @@ .app-docs-content h5, .app-docs-content h6 { scroll-margin-top: 1.5rem; - line-height: 1.3; + line-height: var(--leading-snug); } .app-docs-content h1 { color: var(--app-h1-color); margin-top: 0; - font-size: 2.75rem; + font-size: var(--text-4xl); } .app-docs-content h2 { color: var(--app-h2-color); - font-size: 1.375rem; + font-size: var(--text-xl); margin-top: 2.5rem; padding-top: 1rem; border-top: 1px solid var(--app-border); } .app-docs-content h3 { color: var(--app-h3-color); - font-size: 1.125rem; + font-size: var(--text-lg); margin-top: 1.75rem; } .app-docs-content h4 { color: var(--app-h4-color); - font-size: 1rem; + font-size: var(--text-base); margin-top: 1.25rem; } .app-docs-content h5, .app-docs-content h6 { color: var(--app-h5-color); - font-size: 0.9rem; + font-size: var(--text-sm); margin-top: 1rem; } .app-docs-content p { margin-block: 0.875rem; - line-height: 1.7; + line-height: var(--leading-loose); } .app-docs-content a { @@ -268,7 +268,7 @@ .app-docs-content li { margin-block: 0.3rem; - line-height: 1.6; + line-height: var(--leading-relaxed); } .app-docs-content li > ul, @@ -314,7 +314,7 @@ .app-docs-content pre code { background: none; padding: 0; - font-size: 0.85rem; + font-size: var(--text-sm); color: var(--app-color); } @@ -341,7 +341,7 @@ .app-docs-content table { width: 100%; border-collapse: collapse; - font-size: 0.875rem; + font-size: var(--text-sm); margin-block: 1.25rem; overflow-x: auto; display: block; @@ -356,7 +356,7 @@ .app-docs-content th { background: var(--app-code-background-color); - font-weight: 600; + font-weight: var(--font-semibold); color: var(--app-color); } diff --git a/web/css/pages/app-error.css b/web/css/pages/app-error.css index 59e627d..d26f749 100644 --- a/web/css/pages/app-error.css +++ b/web/css/pages/app-error.css @@ -9,15 +9,15 @@ } .error-page__code { - font-size: 5rem; - font-weight: 700; - line-height: 1; + font-size: var(--text-5xl); + font-weight: var(--font-bold); + line-height: var(--leading-none); color: var(--color-text-muted); } .error-page__message { margin-top: 1rem; - font-size: 1.125rem; + font-size: var(--text-lg); color: var(--color-text); } @@ -25,7 +25,7 @@ display: inline-block; margin-top: 2rem; padding: 0.5rem 1.25rem; - font-size: 0.875rem; + font-size: var(--text-sm); text-decoration: none; border: 1px solid var(--color-border); border-radius: 0.375rem; diff --git a/web/css/pages/app-login.css b/web/css/pages/app-login.css index 3da9906..fe992e5 100644 --- a/web/css/pages/app-login.css +++ b/web/css/pages/app-login.css @@ -596,7 +596,7 @@ border: 0; color: var(--app-muted-color); cursor: pointer; - font-size: 1rem; + font-size: var(--text-base); border-radius: var(--app-border-radius); transition: color 0.15s ease; } @@ -646,7 +646,7 @@ .login-tenant-select-label { display: block; margin-bottom: 0.5rem; - font-weight: 600; + font-weight: var(--font-semibold); } .login-tenant-choice-grid { @@ -717,8 +717,8 @@ align-items: center; justify-content: flex-start; padding-left: 0.5rem; - font-size: 0.875rem; - font-weight: 600; + font-size: var(--text-sm); + font-weight: var(--font-semibold); color: var(--app-contrast); } @@ -743,7 +743,7 @@ z-index: 1; display: inline-block; padding: 0 0.75rem; - font-size: 0.875rem; + font-size: var(--text-sm); color: var(--app-muted-color); background: var(--app-card-background-color); } @@ -795,8 +795,8 @@ display: inline-flex; align-items: center; justify-content: center; - font-size: 0.875rem; - font-weight: 700; + font-size: var(--text-sm); + font-weight: var(--font-bold); color: var(--app-primary); } @@ -806,7 +806,7 @@ height: auto; justify-content: flex-start; text-align: left; - font-weight: 700; + font-weight: var(--font-bold); color: var(--app-text); } @@ -817,7 +817,7 @@ .login-tenant-context-name { margin: 0; - font-size: 11px; + font-size: var(--text-xs); white-space: nowrap; text-overflow: ellipsis; max-width: 130px; @@ -843,7 +843,7 @@ padding: 0; gap: 10px; - font-size: 0.72rem; + font-size: var(--text-xs); } .login-help-links-item { @@ -854,20 +854,20 @@ } .login-security-note { - font-size: 0.72rem; + font-size: var(--text-xs); margin-bottom: 1px; color: color-mix(in srgb, var(--app-contrast) 78%, var(--app-muted-color)); } .login-security-note i { color: var(--app-primary); - font-size: 0.85rem; + font-size: var(--text-sm); } .login-security-note-detail { width: 100%; display: block; - font-size: 9px; + font-size: var(--text-2xs); color: color-mix(in srgb, var(--app-muted-color) 75%, transparent); }