asdfdas
This commit is contained in:
1 parent
f84687d745
commit
7508f6e084
9 files changed
+55
-14
No files matched your search
@@ -2,8 +2,8 @@
|
||||
import Wordmark from './Wordmark.astro';
|
||||
import { localePath, t, type Lang } from '../i18n/ui';
|
||||
|
||||
interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
interface Props { lang: Lang; tone?: 'paper' | 'night' }
|
||||
const { lang, tone = 'paper' } = Astro.props;
|
||||
const ui = t(lang);
|
||||
const home = localePath(lang, '/');
|
||||
const items = [
|
||||
@@ -16,7 +16,7 @@ const contactHref = `${home}#contact`;
|
||||
const path = Astro.url.pathname.replace(/\/$/, '') || '/';
|
||||
const isCurrent = (href: string) => href.split('#')[0] === path && !href.includes('#');
|
||||
---
|
||||
<header class="header" data-header data-tone="paper">
|
||||
<header class="header" data-header data-tone={tone} data-tone-init>
|
||||
<div class="container header__shell">
|
||||
<div class="header__bar hud-frame">
|
||||
<a class="header__home" href={home} aria-label={ui.nav.homeLabel}>
|
||||
@@ -102,6 +102,12 @@ const isCurrent = (href: string) => href.split('#')[0] === path && !href.include
|
||||
background: var(--glass-solid);
|
||||
transition: background-color var(--dur) var(--ease);
|
||||
}
|
||||
/* Until the script has settled the tone on load, it changes without a fade: the tone is a fact
|
||||
about where the page is, not a change the visitor made. */
|
||||
.header[data-tone-init],
|
||||
.header[data-tone-init] *,
|
||||
.header[data-tone-init] ::before,
|
||||
.header[data-tone-init] ::after { transition: none !important; }
|
||||
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
|
||||
.header__bar::before,
|
||||
.header__nav[data-open]::before {
|
||||
@@ -242,6 +248,8 @@ const isCurrent = (href: string) => href.split('#')[0] === path && !href.include
|
||||
/* Tone: watch a one-pixel line through the middle of the bar and switch to night while a
|
||||
.night section crosses it. An IntersectionObserver, so nothing runs on scroll. */
|
||||
const sections = document.querySelectorAll<HTMLElement>('.night');
|
||||
// Transitions come back once the first tone is drawn (two frames, so it has been painted).
|
||||
const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => header?.removeAttribute('data-tone-init')));
|
||||
if (header && sections.length && 'IntersectionObserver' in window) {
|
||||
const under = new Set<Element>();
|
||||
let observer: IntersectionObserver | undefined;
|
||||
@@ -254,6 +262,7 @@ const isCurrent = (href: string) => href.split('#')[0] === path && !href.include
|
||||
if (entry.isIntersecting) under.add(entry.target); else under.delete(entry.target);
|
||||
}
|
||||
header.dataset.tone = under.size ? 'night' : 'paper';
|
||||
if (header.hasAttribute('data-tone-init')) settle();
|
||||
}, { rootMargin: `-${mid}px 0px -${window.innerHeight - mid - 1}px 0px` });
|
||||
sections.forEach((section) => observer!.observe(section));
|
||||
};
|
||||
@@ -263,5 +272,7 @@ const isCurrent = (href: string) => href.split('#')[0] === path && !href.include
|
||||
window.clearTimeout(resize);
|
||||
resize = window.setTimeout(watch, 150);
|
||||
});
|
||||
} else {
|
||||
settle();
|
||||
}
|
||||
</script>
|
||||
@@ -54,7 +54,7 @@ const f = ui.contact.form;
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- The form's answer, as a toast in the lower left. Framed like the menu, on night. -->
|
||||
<!-- The form's answer, as a toast in the lower right. Framed like the menu, on night. -->
|
||||
<div
|
||||
class="toast hud-frame"
|
||||
data-toast
|
||||
|
||||
@@ -71,14 +71,19 @@ const m = ui.insights.mention;
|
||||
.insights__card:hover h3 { text-decoration: underline; text-decoration-thickness: var(--hairline); text-underline-offset: 0.12em; }
|
||||
.insights__img, :global(.insights__img) { width: 100%; height: auto; aspect-ratio: 4 / 1; object-fit: cover; object-position: 50% 15%; margin-bottom: var(--space-2); }
|
||||
.insights__card h3 { max-width: 18em; }
|
||||
/* No picture: a colour field carries the source in large type. */
|
||||
/* No picture: a colour field carries the source in large type, centred in the short strip,
|
||||
with the field's darkening moved behind it. */
|
||||
:global(.insights__panel) {
|
||||
--field-scrim: var(--field-scrim-centre);
|
||||
aspect-ratio: 4 / 1;
|
||||
margin-bottom: var(--space-2);
|
||||
font-size: var(--step-title);
|
||||
line-height: var(--leading-title);
|
||||
letter-spacing: var(--track-h2);
|
||||
}
|
||||
/* Field's own scoped rule (a class and an attribute) puts its text at the bottom; three
|
||||
classes outrank it whatever order the stylesheets load in. */
|
||||
:global(.insights__card .field.insights__panel) { justify-content: center; }
|
||||
@media (min-width: 48rem) {
|
||||
.insights__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
@@ -93,8 +93,7 @@ const now = 0;
|
||||
position: absolute;
|
||||
inset: 0.2rem;
|
||||
border-radius: 50%;
|
||||
background: var(--signal);
|
||||
outline: var(--hairline) solid var(--ink);
|
||||
background: var(--ink);
|
||||
}
|
||||
.track__step[data-state='next'] .track__node { border-style: dashed; border-color: var(--ink-muted); }
|
||||
.track__state { font-size: var(--step-small); line-height: var(--leading-small); color: var(--ink-muted); }
|
||||
|
||||
@@ -168,7 +168,7 @@ const ridges = Array.from({ length: rows }, (_, r) => {
|
||||
display: grid;
|
||||
gap: var(--space-5);
|
||||
padding: var(--space-6);
|
||||
background: var(--glass-on-night);
|
||||
background: var(--panel-on-night);
|
||||
align-content: start;
|
||||
justify-items: start;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ const ridges = Array.from({ length: rows }, (_, r) => {
|
||||
letter-spacing: var(--track-caps);
|
||||
text-transform: uppercase;
|
||||
line-height: var(--leading-small);
|
||||
background: var(--glass-on-night);
|
||||
background: var(--panel-on-night);
|
||||
}
|
||||
.readout__cell {
|
||||
display: grid;
|
||||
@@ -221,7 +221,23 @@ const ridges = Array.from({ length: rows }, (_, r) => {
|
||||
.readout__cell + .readout__cell { border-top: var(--hairline) solid var(--line-on-night); }
|
||||
.readout__cell--head, .readout dt { color: var(--ink-muted); }
|
||||
.readout dd { display: flex; align-items: center; gap: var(--space-2); }
|
||||
.readout__live { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--signal); flex: none; }
|
||||
.readout__live { position: relative; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--signal); flex: none; }
|
||||
/* The live phase pulses: a ring in the signal tone grows out of the dot and fades. */
|
||||
.readout__live::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background: var(--signal);
|
||||
animation: readout-pulse calc(6 * var(--dur-slow)) var(--ease) infinite;
|
||||
}
|
||||
@keyframes readout-pulse {
|
||||
from { transform: scale(1); opacity: 0.7; }
|
||||
70%, to { transform: scale(3); opacity: 0; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.readout__live::after { animation: none; opacity: 0; }
|
||||
}
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
.signup { padding: var(--space-5); }
|
||||
@@ -242,5 +258,7 @@ const ridges = Array.from({ length: rows }, (_, r) => {
|
||||
.shero__name { grid-column: 1 / -1; margin-top: var(--space-4); }
|
||||
.readout__cell { grid-template-columns: 1fr; gap: var(--space-1); padding: var(--space-4) var(--space-5); }
|
||||
.readout__cell + .readout__cell { border-top: 0; border-left: var(--hairline) solid var(--line-on-night); }
|
||||
/* The strip's title sits in the middle of its cell, apart from the label/value cells beside it. */
|
||||
.readout__cell--head { place-content: center; text-align: center; }
|
||||
}
|
||||
</style>
|
||||
@@ -11,10 +11,13 @@ interface Props {
|
||||
ogImage?: string;
|
||||
/** Pages that only make sense as the answer to an action, such as the contact form result. */
|
||||
noindex?: boolean;
|
||||
/** The surface the page opens on. 'night' for pages that start on a dark hero, so the header
|
||||
is drawn in its night tone from the first frame instead of switching after load. */
|
||||
tone?: 'paper' | 'night';
|
||||
}
|
||||
|
||||
const lang: Lang = Astro.props.lang ?? langFromUrl(Astro.url);
|
||||
const { title, description, noindex } = Astro.props;
|
||||
const { title, description, noindex, tone = 'paper' } = Astro.props;
|
||||
const ui = t(lang);
|
||||
const fullTitle = title === ui.meta.home.title ? title : `${title}. ${ui.meta.siteName}`;
|
||||
const canonical = new URL(Astro.url.pathname, Astro.site);
|
||||
@@ -48,7 +51,7 @@ const altDe = new URL(switchLang(Astro.url, 'de'), Astro.site);
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">{ui.nav.skip}</a>
|
||||
<Header lang={lang} />
|
||||
<Header lang={lang} tone={tone} />
|
||||
<main id="main" tabindex="-1">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
@@ -15,7 +15,7 @@ interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
const ui = t(lang);
|
||||
---
|
||||
<Base lang={lang} title={ui.meta.home.title} description={ui.meta.home.description}>
|
||||
<Base lang={lang} title={ui.meta.home.title} description={ui.meta.home.description} tone="night">
|
||||
<Hero lang={lang} />
|
||||
<Manifesto lang={lang} />
|
||||
<Method lang={lang} />
|
||||
|
||||
@@ -15,7 +15,7 @@ interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
const ui = t(lang);
|
||||
---
|
||||
<Base lang={lang} title={ui.meta.verifier.title} description={ui.meta.verifier.description}>
|
||||
<Base lang={lang} title={ui.meta.verifier.title} description={ui.meta.verifier.description} tone="night">
|
||||
{verifierComingSoon ? (
|
||||
<>
|
||||
<SoonHero lang={lang} />
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
--field-sky: #6AAEE8;
|
||||
/* Darkens the lower edge of a field so paper-coloured type on it holds 3:1 or better. */
|
||||
--field-scrim: linear-gradient(to top, rgb(14 16 15 / 0.55), rgb(14 16 15 / 0) 65%);
|
||||
/* The same for a field whose text sits centred on the left (the home page Insights cards). */
|
||||
--field-scrim-centre: radial-gradient(90% 85% at 20% 50%, rgb(14 16 15 / 0.72), rgb(14 16 15 / 0) 100%);
|
||||
|
||||
/* Signal: the one status tone the brief allows. Used only for a live state (the coming-soon
|
||||
page's current phase), at most twice per page. Never for text, links or decoration. */
|
||||
@@ -146,6 +148,9 @@
|
||||
--glass-on-night: rgb(14 16 15 / 0.56);
|
||||
--glass-solid: rgb(233 229 223 / 0.96);
|
||||
--glass-solid-on-night: rgb(14 16 15 / 0.94);
|
||||
/* Panels over a busy night background (the product page's sign-up and status strip): darker
|
||||
than the menu glass, so the lines behind only just show through. */
|
||||
--panel-on-night: rgb(14 16 15 / 0.85);
|
||||
--glass-blur: 14px;
|
||||
/* Tints mixed from the text colour, so they work on either tone. */
|
||||
--tint-hover: 6%;
|
||||
|
||||
Reference in new issue
Block a user