feat(ui): g100 dark theme and more space above page titles

- all pages (landing, both tools, server error pages) render on Carbon's
  g100 theme via <body class="app cds--g100">; token consumers follow
  automatically, including component shadow styles through the inherited
  --cds-* custom properties
- dark base on <html> so no light flash around the viewport
- step chips and tool icons use the brand blue: layer-selected-inverse
  inverts to a LIGHT token in g100, which had made them white-on-white
- page titles breathe: .page-head top margin 1.5rem -> 2.75rem
- new test asserting every page renders on g100 (44 total)
This commit is contained in:
2026-10-01 04:01:44 +01:00
parent 340d8da19d
commit 17c407883d
6 changed files with 24 additions and 7 deletions
+1 -1
View File
@@ -22,7 +22,7 @@
.review-options .form-check { margin-bottom: 0.35rem; } .review-options .form-check { margin-bottom: 0.35rem; }
</style> </style>
</head> </head>
<body class="app"> <body class="app cds--g100">
<div id="app"> <div id="app">
<!--app-header--> <!--app-header-->
+1 -1
View File
@@ -15,7 +15,7 @@
<script type="module" src="https://1.www.s81c.com/common/carbon/web-components/version/v2.50.0/progress-bar.min.js"></script> <script type="module" src="https://1.www.s81c.com/common/carbon/web-components/version/v2.50.0/progress-bar.min.js"></script>
<script type="module" src="https://1.www.s81c.com/common/carbon/web-components/version/v2.50.0/tag.min.js"></script> <script type="module" src="https://1.www.s81c.com/common/carbon/web-components/version/v2.50.0/tag.min.js"></script>
</head> </head>
<body class="app"> <body class="app cds--g100">
<div id="app"> <div id="app">
<!--app-header--> <!--app-header-->
+4 -3
View File
@@ -6,6 +6,7 @@
[v-cloak] { display: none; } [v-cloak] { display: none; }
a { text-decoration: none; } a { text-decoration: none; }
html { background: var(--cds-background, #161616); }
body.app { body.app {
font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: var(--cds-background, #f4f4f4); background: var(--cds-background, #f4f4f4);
@@ -59,7 +60,7 @@ cds-side-nav[expanded] { pointer-events: auto; }
main.container { flex: 1 0 auto; padding-top: 1.5rem; padding-bottom: 1.5rem; } main.container { flex: 1 0 auto; padding-top: 1.5rem; padding-bottom: 1.5rem; }
.narrow { max-width: 560px; } .narrow { max-width: 560px; }
.center-v { display: flex; flex-direction: column; justify-content: center; min-height: 70vh; } .center-v { display: flex; flex-direction: column; justify-content: center; min-height: 70vh; }
.page-head { margin: 1.5rem auto 1.5rem; display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; } .page-head { margin: 2.75rem auto 1.5rem; display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.page-head h1 { font-size: 1.5rem; font-weight: 600; margin: 0; letter-spacing: 0; } .page-head h1 { font-size: 1.5rem; font-weight: 600; margin: 0; letter-spacing: 0; }
.page-head .sub { color: var(--cds-text-secondary, #525252); margin: 0.25rem 0 0; font-size: 14px; } .page-head .sub { color: var(--cds-text-secondary, #525252); margin: 0.25rem 0 0; font-size: 14px; }
@@ -73,7 +74,7 @@ main.container { flex: 1 0 auto; padding-top: 1.5rem; padding-bottom: 1.5rem; }
.panel-head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 1rem; flex-wrap: wrap; } .panel-head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 1rem; flex-wrap: wrap; }
.step-chip { .step-chip {
width: 22px; height: 22px; border-radius: 2px; flex: 0 0 auto; width: 22px; height: 22px; border-radius: 2px; flex: 0 0 auto;
background: var(--cds-layer-selected-inverse, #161616); color: #f4f4f4; background: var(--cds-button-primary, #0f62fe); color: #fff;
font-size: 12px; font-weight: 600; font-size: 12px; font-weight: 600;
display: inline-flex; align-items: center; justify-content: center; display: inline-flex; align-items: center; justify-content: center;
} }
@@ -93,7 +94,7 @@ main.container { flex: 1 0 auto; padding-top: 1.5rem; padding-bottom: 1.5rem; }
.tool-icon { .tool-icon {
width: 40px; height: 40px; border-radius: 4px; margin: 0; width: 40px; height: 40px; border-radius: 4px; margin: 0;
display: inline-flex; align-items: center; justify-content: center; display: inline-flex; align-items: center; justify-content: center;
background: var(--cds-layer-selected-inverse, #161616); color: #fff; background: var(--cds-button-primary, #0f62fe); color: #fff;
} }
.tool-icon svg { width: 20px; height: 20px; } .tool-icon svg { width: 20px; height: 20px; }
.tool-card .go { margin-top: auto; color: var(--cds-link, #0f62fe); font-weight: 600; font-size: 13px; display: inline-flex; align-items: center; gap: 0.3rem; } .tool-card .go { margin-top: auto; color: var(--cds-link, #0f62fe); font-weight: 600; font-size: 13px; display: inline-flex; align-items: center; gap: 0.3rem; }
+1 -1
View File
@@ -15,7 +15,7 @@
cds-tile:not(:defined), cds-clickable-tile:not(:defined) { visibility: hidden; } cds-tile:not(:defined), cds-clickable-tile:not(:defined) { visibility: hidden; }
</style> </style>
</head> </head>
<body class="app"> <body class="app cds--g100">
<!--app-header--> <!--app-header-->
<main class="container"> <main class="container">
+1 -1
View File
@@ -74,7 +74,7 @@ function errorPage(res, status, title, message, actions = {}) {
<title>${esc(title)} · YOUreka Tools</title> <title>${esc(title)} · YOUreka Tools</title>
<link rel="stylesheet" href="/assets/style.css"> <link rel="stylesheet" href="/assets/style.css">
</head> </head>
<body class="app"> <body class="app cds--g100">
${appHeader(null, null)} ${appHeader(null, null)}
<main class="container narrow center-v"> <main class="container narrow center-v">
<div class="card message-card"> <div class="card message-card">
+16
View File
@@ -312,6 +312,22 @@ test('injected session values cannot break out of the script tag', async () => {
} }
}); });
test('all pages render on the g100 dark theme', async () => {
const { base, stop } = await bootApp();
try {
const cookies = await login(base);
for (const path of ['/', '/fingerprint']) {
const html = await (await fetch(`${base}${path}`, { headers: { cookie: cookies } })).text();
assert.match(html, /<body class="app cds--g100">/, `${path} uses g100`);
}
// error pages too
const errorHtml = await (await fetch(`${base}/auth/callback?sessionId=nope`, manual)).text();
assert.match(errorHtml, /<body class="app cds--g100">/);
} finally {
await stop();
}
});
test('landing header is server-rendered for the session', async () => { test('landing header is server-rendered for the session', async () => {
const { base, stop } = await bootApp(); const { base, stop } = await bootApp();
try { try {