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
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Landing Page: Unified Background for Hero + Showcase

## Goal
Make the Voily landing page feel like one continuous scene by using `hero-bg-a.png` as a single background that wraps both the hero section and the app interface showcase (DashboardWindow). Also improve text contrast by switching the hero copy from gray tones to near-black.

## Context
- The current landing page (`website/src/components/LandingPage.tsx`) only applies the preview background image to the `.hero` section when the `?bg=a` query parameter is present.
- The showcase section sits directly below the hero with a negative margin overlap, creating the visual impression that the app dashboard peeks up from the hero.
- There are three candidate background images in `website/public/`:
- `hero-bg-a.png` (6144×4096): blue sky, grassy hill, foreground flowers — selected for this design.
- `hero-bg-b.png` (5760×4096): very light watercolor wash.
- `hero-bg-c.png` (5760×4096): dark grass with a digital-matrix overlay.
- The image height of 4096 px is more than enough to cover the combined height of the hero (100 vh in preview mode) plus the showcase/dashboard region on all common viewports.

## Design Decisions

### 1. Single wrapper container
Introduce a new wrapper element around `.hero` and `.showcase` and move the background image styling from `.hero` to this wrapper.

```tsx
<div
className="hero-showcase-combo"
style={
heroBg
? {
backgroundImage: `url(/hero-bg-${heroBg}.png)`,
backgroundSize: 'cover',
backgroundPosition: 'center'
}
: undefined
}
>
<section className="hero">…</section>
<section className="showcase">…</section>
</div>
```

When no `?bg` parameter is present, the wrapper has no background and the existing `DotFieldBackground` continues to render inside the hero as before.

### 2. Preserve hero readability
Keep the existing `.hero::after` gradient overlay so the headline remains legible over the busy sky/grass background.

For preview mode, the overlay is currently removed via `.hero.hero-preview-bg::after { content: none; }`. When the background spans both hero and showcase, we may want to keep a very light overlay on the hero even in preview mode, or keep the existing removal and rely on the stronger text colors described below. This is left to implementation testing.

### 3. Dashboard readability scrim
The lower portion of `hero-bg-a` contains grass and flowers with medium-to-dark tones. The dashboard window must remain clearly readable while still letting the background feel connected. Apply a subtle frosted-glass treatment to the dashboard window only (so feature-section windows further down the page are not affected):

```css
.dashboard-window {
background: rgba(255, 255, 255, 0.86);
backdrop-filter: blur(10px);
/* border, border-radius and shadow remain unchanged */
}
```

The exact opacity and blur radius should be tuned during implementation so the background is still visible through the chrome but dashboard text/charts are crisp. If this proves too subtle, fall back to a light scrim layer behind `.showcase-frame` with `border-radius: 16px` to match `.app-window`.

### 4. Hero text color to near-black
The current hero text uses muted grays (`var(--faint)`, `#3f4248`, `#7a7f86`). Against the photographic background these colors can look washed out. Switch them to a strong near-black:

- `.hero h1` and `.hero h1 span` → `#090909` (or `--ink` if it maps to a near-black)
- `.hero p` → `#090909`
- `.hero-meta` → `#090909`

The primary CTA button styles remain unchanged (black fill, white text).

### 5. Maintain existing interactions
- The scroll cue arrow inside `.showcase` must remain visible and clickable.
- The fixed site header behavior should be preserved; in preview mode the header stays transparent/absolute as today.
- Existing responsive breakpoints and animations should not regress.

## Files to Modify
1. `website/src/components/LandingPage.tsx` — add the wrapper element and move the inline background style.
2. `website/src/styles.css` — add `.hero-showcase-combo` rules, adjust `.showcase-frame` scrim, and update hero text colors.

## Verification
- Open the landing page with `?bg=a` and confirm the background flows continuously from hero into showcase.
- Verify the headline, subheadline, meta line, and dashboard content are all readable.
- Test on common widths: 1440 px, 1280 px, 768 px, 375 px.
- Confirm the default view (no `?bg`) still shows the dot-field background and the unchanged light text colors.

## Risks & Open Questions
- `background-size: cover` means the crop point shifts with viewport aspect ratio. We may need to tune `background-position` (e.g., `center 30%`) so the hill/grass transition sits nicely behind the dashboard.
- If the user later wants the same treatment for `hero-bg-b` or `hero-bg-c`, the dashboard scrim opacity may need per-image adjustment.
Binary file added website/public/hero-bg-b.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 39 additions & 26 deletions website/src/components/LandingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { apps, openSourceBenefits } from './content'
import { DashboardWindow } from './DashboardWindow'
import { Features } from './Features'
import { DotFieldBackground } from './DotFieldBackground'

const downloadUrl = 'https://github.com/BubblePtr/Voily/releases/latest'
const githubUrl = 'https://github.com/BubblePtr/Voily'

const heroBg = 'b'

function GitHubIcon() {
return (
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
Expand All @@ -19,7 +20,7 @@ function GitHubIcon() {

export function LandingPage() {
return (
<main className="page">
<main className="page preview-mode">
<header className="site-header">
<a className="brand" href="#top" aria-label="Voily home">
<img src="/voily-icon.png" alt="" />
Expand All @@ -37,31 +38,43 @@ export function LandingPage() {
</div>
</header>

<section className="hero" id="top" aria-labelledby="hero-title">
<DotFieldBackground />
<div className="hero-copy">
<h1 id="hero-title" aria-label="Just speak. We'll handle the rest.">
<span>Just speak.</span>
<span>We'll handle the rest.</span>
</h1>
<p>From raw voice to refined text, right where your cursor is.</p>
<a className="primary-cta" href={downloadUrl}>
Download for macOS
</a>
<span className="hero-meta">
macOS 14+ · Apple Silicon · Open Source
</span>
</div>
</section>
<div
className="hero-showcase-combo"
style={{
backgroundImage: `url(/hero-bg-${heroBg}.png)`,
backgroundSize: 'cover',
backgroundPosition: 'center'
}}
>
<section
className="hero hero-preview-bg"
id="top"
aria-labelledby="hero-title"
>
<div className="hero-copy">
<h1 id="hero-title" aria-label="Just speak. We'll handle the rest.">
<span>Just speak.</span>
<span>We'll handle the rest.</span>
</h1>
<p>From raw voice to refined text, right where your cursor is.</p>
<a className="primary-cta" href={downloadUrl}>
Download for macOS
</a>
<span className="hero-meta">
macOS 14+ · Apple Silicon · Open Source
</span>
</div>
</section>

<section className="showcase" id="showcase" aria-label="Voily app">
<div className="showcase-frame">
<DashboardWindow />
</div>
<a className="scroll-cue" href="#features" aria-label="Scroll to features">
<span />
</a>
</section>
<section className="showcase" id="showcase" aria-label="Voily app">
<div className="showcase-frame">
<DashboardWindow />
</div>
<a className="scroll-cue" href="#features" aria-label="Scroll to features">
<span />
</a>
</section>
</div>

<Features />

Expand Down
35 changes: 32 additions & 3 deletions website/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ a {
box-shadow: 0 28px 60px rgba(0, 0, 0, 0.18);
}

.hero-showcase-combo {
position: relative;
}

.hero {
position: relative;
display: grid;
Expand Down Expand Up @@ -186,6 +190,25 @@ a {
pointer-events: none;
}

.hero.hero-preview-bg {
min-height: 100vh;
image-rendering: -webkit-optimize-contrast;
}

.hero.hero-preview-bg::after {
content: none;
}

.preview-mode .site-header {
position: absolute;
background: transparent;
backdrop-filter: none;
}

.preview-mode .showcase {
margin-top: clamp(-520px, -36vh, -260px);
}

.hero-copy {
position: relative;
z-index: 1;
Expand All @@ -212,13 +235,13 @@ a {
}

.hero h1 span + span {
color: var(--faint);
color: #090909;
}

.hero p {
margin: 30px 0 0;
max-width: 680px;
color: #3f4248;
color: #090909;
font-size: clamp(18px, 1.8vw, 24px);
line-height: 1.58;
}
Expand All @@ -230,7 +253,7 @@ a {
.hero-meta {
display: block;
margin-top: 17px;
color: #7a7f86;
color: #090909;
font-size: 15px;
font-weight: 600;
}
Expand Down Expand Up @@ -669,6 +692,12 @@ a {
0 10px 30px rgba(15, 20, 28, 0.06);
}

.dashboard-window {
background: rgba(255, 255, 255, 0.86);
backdrop-filter: blur(10px);
border-color: rgba(230, 232, 236, 0.8);
}

.win-toolbar {
display: flex;
align-items: center;
Expand Down
Loading