Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/open-lab-banner/screenshots/banner-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions docs/open-lab-banner/verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Open Lab homepage invitation

A small website-only follow-up to the old embedded prototype in PR #162. The existing approved invitation copy and placement (after the hero, before Focus Areas) are preserved. The destination is now exactly **https://open-lab-two.vercel.app/**, the standalone app's stable Vercel origin.

The link is a normal native anchor: same-tab navigation, keyboard Enter, browser modifiers/new-tab and no-JavaScript fallback work without an app integration. This change does not bring `/lab/` routes, OAuth, app dependencies, DNS or infrastructure into the website. The optional earlier circular departure animation is not included.

## Verification

Runtime source: `a88b10e54ecbf36b7e96f71fb482aaf329d75ae3` (including the final banner-label contrast correction).

- pnpm 10 frozen install passed. No package/lockfile changes.
- Full standard suite: **116 passed, 0 failed, 0 skipped**, including a new RED/GREEN homepage placement, exact destination and native-anchor test.
- TypeScript passed.
- Production build passed. Local verification temporarily capped Next's build worker count at 1; that operational cap was restored before commit and is not in the PR. Existing upstream data-cache/closed-round snapshot warnings occurred; this banner does not repair those independent sources.
- Actual production-built localhost page: one banner with exact destination, correct placement; 1440px light/dark and 390px checks and screenshots. The dark capture was taken after the existing theme transition settled.
- Native keyboard Enter from the banner reached `https://open-lab-two.vercel.app/lab/` in the headed browser. Direct HTTPS root also returned HTTP 200 after redirecting there; the real hosted sign-in form was visible. No account sign-in or public write was attempted.

- Independent candidate review found one blocker: the original 12px light-mode label had 3.54:1 contrast. Fixed using the existing banner-local `text-dark-blue` token while retaining `dark:text-blue`; RED/GREEN regression now checks actual palette tokens. Final fresh-skeptical review of that finding and recaptured production-browser screenshots passed: measured 6.38:1 light and 5.40:1 dark, readable mobile wrapping, no clipping or destination change. Full suite and build were repeated after the correction.

Screenshots in `screenshots/` are the actual locally served website at the runtime revision above, not hosted deploy-preview screenshots. No production website merge is authorized by this PR preparation.
28 changes: 28 additions & 0 deletions scripts/homepage-order.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,34 @@ function text(node) {
return node && typeof node === 'object' ? text(node.props?.children) : ''
}

test('homepage invites visitors to the standalone Open Lab between the hero and focus areas', async (t) => {
// Only the remote editable-copy boundary is mocked; inspect the real homepage.
t.mock.method(globalThis, 'fetch', async () => Response.json({
data: { orgPlresearchPage: { edges: [] } },
}))
const nodes = elements(await HomePage())
const banners = nodes.filter((node) => node.type === 'a' && text(node).includes('Introducing Open Lab'))
assert.equal(banners.length, 1, 'render exactly one native Open Lab invitation link')
const banner = banners[0]
assert.equal(banner.props.href, 'https://open-lab-two.vercel.app/', 'navigate directly to the separate app origin')
assert.equal(banner.props.onClick, undefined, 'preserve native navigation without JavaScript interception')
assert.equal(banner.props.target, undefined, 'navigate in the current tab by default')
assert.ok(text(banner).includes('Made something that makes science easier?'))
assert.ok(text(banner).includes('Explore the lab →'))
const label = elements(banner).find(node => node.type === 'span' && text(node) === 'Introducing Open Lab')
const { readFileSync } = await import('node:fs')
const css = readFileSync('src/app/globals.css', 'utf8')
const token = name => css.match(new RegExp(`--color-${name}:\\s*(#[0-9a-fA-F]{6})`))[1]
const luminance = hex => { const rgb = hex.slice(1).match(/../g).map(value => parseInt(value, 16) / 255).map(value => value <= .04045 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4); return rgb[0] * .2126 + rgb[1] * .7152 + rgb[2] * .0722 }
const color = label.props.className.split(' ').find(value => /^text-(?:dark-blue|blue)$/.test(value)).slice(5)
const a = luminance(token(color)), b = luminance(token('gray-100'))
assert.ok((Math.max(a, b) + .05) / (Math.min(a, b) + .05) >= 4.5, '12px invitation label must meet normal-text AA contrast')
const heroIndex = nodes.findIndex((node) => node.type === 'h1')
const focusIndex = nodes.findIndex((node) => node.props?.id === 'focus-areas')
assert.ok(heroIndex >= 0 && nodes.indexOf(banner) > heroIndex, 'invitation follows the hero')
assert.ok(focusIndex > nodes.indexOf(banner), 'invitation precedes focus areas')
})

test('homepage places the existing latest carousel between focus areas and the innovation-chasm graphic', async (t) => {
// Only the remote editable-copy boundary is mocked; render the real homepage.
t.mock.method(globalThis, 'fetch', async () => Response.json({
Expand Down
13 changes: 13 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,19 @@ export default async function HomePage() {

</div>

<a
href="https://open-lab-two.vercel.app/"
className="block border-y border-black/10 dark:border-white/15 bg-gray-100 hover:bg-blue/10 transition-colors"
>
<div className="max-w-6xl mx-auto px-6 py-6 flex flex-wrap gap-4 items-center justify-between">
<div>
<span className="text-[12px] uppercase tracking-[.16em] text-dark-blue dark:text-blue mr-4">Introducing Open Lab</span>
<span className="font-serif text-[24px]">Made something that makes science easier?</span>
</div>
<span className="text-[15px] font-semibold">Explore the lab →</span>
</div>
</a>

{/* ── Focus Areas (full-bleed gray) ── */}
<div id="focus-areas" className="bg-gray-100 scroll-mt-20">
<div className="max-w-6xl mx-auto px-6 pb-20 lg:pb-28 pt-16 lg:pt-24">
Expand Down