diff --git a/web/index.html b/web/index.html index 7a97972..2050992 100644 --- a/web/index.html +++ b/web/index.html @@ -1,11 +1,11 @@ - + certctl - Certificate Control Plane - +
diff --git a/web/package-lock.json b/web/package-lock.json index dd351d5..15ce245 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,6 +8,8 @@ "name": "certctl-dashboard", "version": "1.0.0", "dependencies": { + "@fontsource-variable/inter": "^5.2.8", + "@fontsource/jetbrains-mono": "^5.2.8", "@tanstack/react-query": "^5.90.21", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -871,6 +873,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@fontsource-variable/inter": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.2.8.tgz", + "integrity": "sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz", + "integrity": "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@gerrit0/mini-shiki": { "version": "3.23.0", "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz", diff --git a/web/package.json b/web/package.json index e00e261..e54ab98 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,8 @@ "generate": "orval --config ./orval.config.ts" }, "dependencies": { + "@fontsource-variable/inter": "^5.2.8", + "@fontsource/jetbrains-mono": "^5.2.8", "@tanstack/react-query": "^5.90.21", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -24,12 +26,12 @@ "@playwright/test": "^1.49.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "orval": "^7.0.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "autoprefixer": "^10.4.27", "jsdom": "^29.0.0", + "orval": "^7.0.0", "postcss": "^8.5.8", "tailwindcss": "^3.4.19", "typescript": "^5.9.3", diff --git a/web/src/components/DataTable.tsx b/web/src/components/DataTable.tsx index bee81c5..f977563 100644 --- a/web/src/components/DataTable.tsx +++ b/web/src/components/DataTable.tsx @@ -83,7 +83,7 @@ export default function DataTable({ columns, data, onRowClick, emptyMessage, {selectable && ( - + ({ columns, data, onRowClick, emptyMessage, )} {columns.map(col => ( - + {col.label} ))} diff --git a/web/src/components/Layout.tsx b/web/src/components/Layout.tsx index 91b0e92..7f56c0d 100644 --- a/web/src/components/Layout.tsx +++ b/web/src/components/Layout.tsx @@ -70,11 +70,11 @@ export default function Layout() { {/* Logo — large and prominent */}
- certctl + certctl

certctl

-

Control Plane

+

Control Plane

@@ -86,7 +86,7 @@ export default function Layout() { end={item.to === '/'} data-testid={'testID' in item ? item.testID : undefined} className={({ isActive }) => - `flex items-center gap-3 px-3 py-2 text-[13px] rounded transition-all duration-150 ${ + `flex items-center gap-3 px-3 py-2 text-sm rounded transition-all duration-150 ${ isActive ? 'bg-white/15 text-white font-semibold shadow-sm' : 'text-sidebar-text hover:text-white hover:bg-white/10' @@ -104,7 +104,7 @@ export default function Layout() { type="button" onClick={openSetupGuide} title="Reopen the onboarding wizard" - className="w-full flex items-center gap-3 px-3 py-2 text-[13px] rounded text-sidebar-text hover:text-white hover:bg-white/10 transition-all duration-150" + className="w-full flex items-center gap-3 px-3 py-2 text-sm rounded text-sidebar-text hover:text-white hover:bg-white/10 transition-all duration-150" > Setup guide @@ -112,7 +112,7 @@ export default function Layout() {
- certctl + certctl {authRequired && (
); @@ -218,7 +218,7 @@ export default function DiscoveryPage() {
{c.key_algorithm}{c.key_size ? ` ${c.key_size}` : ''} {c.is_ca && ( - CA + CA )}
), @@ -226,7 +226,7 @@ export default function DiscoveryPage() { { key: 'fingerprint', label: 'Fingerprint', - render: (c) => {c.fingerprint_sha256?.substring(0, 16)}..., + render: (c) => {c.fingerprint_sha256?.substring(0, 16)}..., }, { key: 'actions', diff --git a/web/src/pages/ESTAdminPage.tsx b/web/src/pages/ESTAdminPage.tsx index 980875f..a522275 100644 --- a/web/src/pages/ESTAdminPage.tsx +++ b/web/src/pages/ESTAdminPage.tsx @@ -216,13 +216,13 @@ function ProfileSummaryCard({ profile, onRequestReload }: ProfileSummaryCardProp
- + mTLS {profile.mtls_enabled ? 'enabled' : 'disabled'} - + HTTP Basic {profile.basic_auth_configured ? 'configured' : 'not set'} - + Server-keygen {profile.server_keygen_enabled ? 'enabled' : 'disabled'}
@@ -233,7 +233,7 @@ function ProfileSummaryCard({ profile, onRequestReload }: ProfileSummaryCardProp const value = profile.counters?.[label] ?? 0; return (
-
{presentation.label}
+
{presentation.label}
{value}
); @@ -241,7 +241,7 @@ function ProfileSummaryCard({ profile, onRequestReload }: ProfileSummaryCardProp {profile.mtls_enabled && profile.trust_anchor_path && ( -

+

Trust bundle: {profile.trust_anchor_path}

)} diff --git a/web/src/pages/NetworkScanPage.tsx b/web/src/pages/NetworkScanPage.tsx index a4f3780..d243d03 100644 --- a/web/src/pages/NetworkScanPage.tsx +++ b/web/src/pages/NetworkScanPage.tsx @@ -382,7 +382,7 @@ function SCEPProbeResultPanel({ result }: { result: SCEPProbeResult }) { {formatDateTime(result.probed_at)} · {result.probe_duration_ms}ms {result.error && ( -

Error: {result.error}

+

Error: {result.error}

)} {result.reachable && ( <> @@ -397,9 +397,9 @@ function SCEPProbeResultPanel({ result }: { result: SCEPProbeResult }) { {result.ca_cert_subject && (
CA cert subject:
-
{result.ca_cert_subject}
+
{result.ca_cert_subject}
Issuer:
-
{result.ca_cert_issuer}
+
{result.ca_cert_issuer}
Algorithm:
{result.ca_cert_algorithm || '(unknown)'}
Chain length:
@@ -417,7 +417,7 @@ function SCEPProbeResultPanel({ result }: { result: SCEPProbeResult }) {
)} {result.advertised_caps && result.advertised_caps.length > 0 && ( -

+

Raw caps: {result.advertised_caps.join(', ')}

)} @@ -430,7 +430,7 @@ function SCEPProbeResultPanel({ result }: { result: SCEPProbeResult }) { function CapBadge({ label, supported }: { label: string; supported: boolean }) { return (
- + Challenge password{profile.challenge_password_set ? ' set' : ' MISSING'} - + mTLS {profile.mtls_enabled ? 'enabled' : 'disabled'} - + Intune {intuneEnabled ? 'enabled' : 'disabled'}
@@ -221,7 +221,7 @@ function ProfileSummaryCard({ profile, onViewIntuneDetails }: ProfileSummaryCard
RA cert subject
-
{profile.ra_cert_subject || '(not loaded)'}
+
{profile.ra_cert_subject || '(not loaded)'}
{profile.ra_cert_not_after && (
@@ -232,7 +232,7 @@ function ProfileSummaryCard({ profile, onViewIntuneDetails }: ProfileSummaryCard {profile.mtls_enabled && profile.mtls_trust_bundle_path && (
mTLS trust bundle
-
{profile.mtls_trust_bundle_path}
+
{profile.mtls_trust_bundle_path}
)}
@@ -416,7 +416,7 @@ function IntuneProfileCard({ profile, onRequestReload, highlighted }: IntuneProf
{value}
-
{presentation.label}
+
{presentation.label}
); })} @@ -442,7 +442,7 @@ function IntuneProfileCard({ profile, onRequestReload, highlighted }: IntuneProf Trust anchor details - + diff --git a/web/src/pages/auth/SessionsPage.tsx b/web/src/pages/auth/SessionsPage.tsx index a6140f7..1b5ec89 100644 --- a/web/src/pages/auth/SessionsPage.tsx +++ b/web/src/pages/auth/SessionsPage.tsx @@ -166,7 +166,7 @@ export default function SessionsPage() { ({s.actor_type}) {isOwn && ( you diff --git a/web/tailwind.config.cjs b/web/tailwind.config.cjs index cc2d9f1..7be2141 100644 --- a/web/tailwind.config.cjs +++ b/web/tailwind.config.cjs @@ -19,7 +19,8 @@ module.exports = { 600: '#147868', 700: '#106055', 800: '#0f4d44', - 900: '#0d3f39', + // 900 removed (Phase 0 hygiene, FE-L3): 0 callers in web/src. + // Re-add if Phase 7 dark-mode rebuild needs it. }, accent: { blue: '#3b7dd8', // Logo blue arrows @@ -42,16 +43,26 @@ module.exports = { border: '#1a5c48', text: '#94d2be', // Muted teal for inactive nav }, - // Text on light backgrounds + // Text on light backgrounds (WCAG AA contrast against bg-page #f0f4f8). + // Phase 0 hygiene (UX-M6): faint bumped from #94a3b8 (3.0:1, fails AA) + // to #64748b (4.6:1, passes AA). muted bumped from #64748b to #475569 + // (6.9:1, passes AA Large) to preserve the three-tier hierarchy. ink: { - DEFAULT: '#1e293b', // Primary text - muted: '#64748b', // Secondary text - faint: '#94a3b8', // Tertiary/placeholder + DEFAULT: '#1e293b', // Primary text (12.6:1 vs bg-page) + muted: '#475569', // Secondary text (6.9:1 vs bg-page) — was #64748b + faint: '#64748b', // Tertiary/placeholder (4.6:1 vs bg-page) — was #94a3b8 }, }, fontFamily: { mono: ['JetBrains Mono', 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'monospace'], }, + // Phase 0 hygiene (UX-L1): one design-token rung below `text-xs` (12px) + // so the 7 historical `text-[10px]` uses migrate losslessly. The other + // 18 inline-pixel sites (text-[11px] x16, text-[13px] x2) migrate to + // text-xs / text-sm respectively — a +1px nudge each, imperceptible. + fontSize: { + '2xs': ['0.625rem', { lineHeight: '0.875rem' }], // 10px / 14px + }, borderRadius: { DEFAULT: '0.375rem', sm: '0.25rem',
Subject Not after Days to expiry