Skip to content
Merged
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
5 changes: 4 additions & 1 deletion src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ h1, h2, h3, h4, p {

.site-shell {
width: 100%;
overflow-x: hidden;
/* clip (not hidden) contains horizontal overflow without turning the
shell into a scroll container — which would force overflow-y to `auto`
and break the sticky nav's position. */
overflow-x: clip;
min-height: 100vh;
background:
radial-gradient(ellipse at 50% -10%, rgba(138,180,248,0.20) 0%, transparent 52%),
Expand Down
23 changes: 13 additions & 10 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ function Waitlist() {
<Mail size={15} className="signup-field-icon" />
<input name="email" type="email" placeholder="you@example.com" required />
</div>
<button disabled={status === 'loading'}>
<button disabled={status === 'loading'} aria-busy={status === 'loading'}>
{status === 'loading' ? 'Setting up…' : 'Continue'}
<ArrowRight size={15} />
</button>
Expand All @@ -361,15 +361,17 @@ function Waitlist() {

<p className="signup-platform">Requires iPhone · iOS 17 or later</p>

{status === 'success' && (
<p className="form-note">You&apos;re in. We&apos;ll email your TestFlight invite when a spot opens.</p>
)}
{status === 'error' && (
<p className="form-note error">
Something went wrong. Try{' '}
<a href="mailto:m.rafiq2006@icloud.com">m.rafiq2006@icloud.com</a>.
</p>
)}
<div className="waitlist-status" role="status" aria-live="polite">
{status === 'success' && (
<p className="form-note">You&apos;re in. We&apos;ll email your TestFlight invite when a spot opens.</p>
)}
{status === 'error' && (
<p className="form-note error">
Something went wrong. Try{' '}
<a href="mailto:m.rafiq2006@icloud.com">m.rafiq2006@icloud.com</a>.
</p>
)}
</div>
</FadeIn>
</section>
);
Expand All @@ -385,6 +387,7 @@ function SiteShell() {
<AmbientContext.Provider value={ambient.setAmbient}>
<PhoneProvider onAmbientChange={ambient.setAmbient}>
<main className="site-shell">
<a className="skip-link" href="#top">Skip to content</a>
<AmbientBackdrop colors={ambient.colors} front={ambient.front} />
<div className="grain" />

Expand Down
105 changes: 105 additions & 0 deletions src/a11y-polish.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* =====================================================
LUMEN - Accessibility & interaction polish
Keyboard focus visibility, skip link, anchor-scroll
offset under the sticky nav, and reduced-motion-aware
smooth scrolling. Layered on top of the existing chrome
so the visual design is untouched for mouse users.
===================================================== */

:root {
--focus-ring: rgba(138, 180, 248, 0.95);
}

html[data-theme='light'] {
--focus-ring: rgba(79, 111, 208, 0.95);
}

/* ===== KEYBOARD FOCUS VISIBILITY =====
Only shows for keyboard/AT users (:focus-visible), so mouse
clicks stay ring-free. Outline follows each element's own
border-radius in modern browsers, so rounded controls stay round. */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 3px;
}

/* The waitlist input clears its outline explicitly (class specificity),
so restore a visible focus ring for keyboard users at matching weight. */
.waitlist-form input:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 2px;
}

/* ===== SKIP TO CONTENT =====
Off-screen until focused, then slides into the top-left so a
keyboard user can jump past the nav on the first Tab press. */

.skip-link {
position: fixed;
top: 12px;
left: 12px;
z-index: 100;
padding: 10px 16px;
border-radius: 10px;
background: var(--cta-bg, #8ab4f8);
color: var(--cta-text, #0a0c12);
font-size: 13px;
font-weight: 600;
letter-spacing: 0.01em;
box-shadow: 0 12px 30px -12px rgba(138, 180, 248, 0.7);
transform: translateY(-160%);
transition: transform 0.18s ease;
}

.skip-link:focus {
transform: translateY(0);
outline: 2px solid var(--focus-ring);
outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
.skip-link {
transition: none;
}
}

/* ===== ANCHOR-SCROLL OFFSET =====
The nav is sticky, so bare #anchor jumps land section headings
underneath it. Reserve the nav's height at the top of every
scroll so targets settle just below it. */

html {
scroll-padding-top: 88px;
}

@media (max-width: 600px) {
html {
scroll-padding-top: 72px;
}
}

/* ===== SMOOTH IN-PAGE SCROLLING ===== */

@media (prefers-reduced-motion: no-preference) {
html {
scroll-behavior: smooth;
}
}

/* ===== BRAND-TINTED TEXT SELECTION ===== */

::selection {
background: rgba(138, 180, 248, 0.28);
color: var(--text-heading, #f7f9fc);
}

html[data-theme='light'] ::selection {
background: rgba(79, 111, 208, 0.22);
color: var(--text-heading, #0f1322);
}
1 change: 1 addition & 0 deletions src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createRoot } from 'react-dom/client';
import { App } from './App.jsx';
import './lumen-overrides.css';
import './mobile-polish.css';
import './a11y-polish.css';

const root = document.getElementById('root');

Expand Down
2 changes: 1 addition & 1 deletion src/mobile-polish.css
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@

@media (max-width: 600px) {
.site-shell {
overflow-x: hidden;
overflow-x: clip;
}

.nav {
Expand Down
Loading