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
6 changes: 3 additions & 3 deletions .impeccable/design.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@
"narrative": {
"northStar": "The Clarity Lab",
"overview": "Bayan Flow looks like a focused lab instrument: generous whitespace, tinted neutrals, and a single confident blue primary. The visualization canvas owns attention; panels and controls recede until needed.",
"keyCharacteristics": [
"keyCharacteristics": [
"CSS-variable tokens in src/index.css",
"Inter Variable for all UI copy",
"Inter Variable for product UI; Source Serif 4 for landing display headings",
"Phosphor icons only",
"Semantic visualization colors are sacred",
"RTL via rtlManager"
Expand All @@ -125,7 +125,7 @@
},
{
"name": "The Inter Lock Rule",
"body": "Inter is the established brand face. Do not swap fonts without an explicit rebrand request.",
"body": "Inter is the established brand face for product UI. Landing display headings (.landing-h1/.landing-h2) may use Source Serif 4 Variable.",
"section": "typography"
},
{
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ See reference doc for full checklists (JS, Python, pseudocode, sound, insight, t
- Favorite algorithms: up to 20 slots (`FREE_TIER_FAVORITE_SLOT_LIMIT` in `src/constants/personalLearning.js`); stored in Supabase `favorite_algorithms`
- Per-algorithm study notes: stored in Supabase `algorithm_notes`; sanitized HTML via `notesService.js`
- Self-service account deletion: Profile Settings danger zone → Supabase Edge Function `delete-account` (CASCADE removes profile, favorites, notes)
- Video Export: unlimited for Free tier with mandatory watermark; Pro tier adds watermark customization/removal; internal daily abuse guard exists (not user-facing)
- Video Export: unlimited for Free tier with mandatory watermark; Pro tier adds watermark customization/removal
- **Auth PRs must not reduce Free tier functionality** — only Anonymous tier can be further restricted
- **Secrets** — publishable anon key via `VITE_*` only; service role key never in repo or client bundle
- **CSP** — Supabase origin in `connect-src`; Google profile photos in `img-src` (lh3.googleusercontent.com); GIS scripts/connect/frame in respective directives; asserted via `scripts/cspHeaders.js` at build time
Expand Down
47 changes: 36 additions & 11 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,32 @@ colors:
viz-pivot: "#8b5cf6"
typography:
display:
fontFamily: "Inter Variable, Inter, system-ui, sans-serif"
fontWeight: 700
fontFamily: "Source Serif 4 Variable, Source Serif 4, Georgia, Times New Roman, serif"
fontWeight: 800
fontSize: "3.5rem"
lineHeight: 1.1
letterSpacing: "-0.02em"
displayMobile:
fontFamily: "Source Serif 4 Variable, Source Serif 4, Georgia, Times New Roman, serif"
fontWeight: 800
fontSize: "2.5rem"
lineHeight: 1.1
heading:
fontFamily: "Source Serif 4 Variable, Source Serif 4, Georgia, Times New Roman, serif"
fontWeight: 700
fontSize: "2.5rem"
lineHeight: 1.2
letterSpacing: "-0.01em"
headingMobile:
fontFamily: "Source Serif 4 Variable, Source Serif 4, Georgia, Times New Roman, serif"
fontWeight: 700
fontSize: "2rem"
lineHeight: 1.2
landingBody:
fontFamily: "Inter Variable, Inter, system-ui, sans-serif"
fontWeight: 400
fontSize: "1.125rem"
lineHeight: 1.7
body:
fontFamily: "Inter Variable, Inter, system-ui, sans-serif"
fontWeight: 400
Expand All @@ -35,6 +58,8 @@ typography:
fontSize: "0.875rem"
letterSpacing: "0.02em"
rounded:
xs: "0.25rem"
scrollbar: "3px"
sm: "0.375rem"
md: "0.5rem"
lg: "0.75rem"
Expand Down Expand Up @@ -109,21 +134,21 @@ A cool slate-and-blue product palette with saturated accents reserved for algori

## Typography

**Display Font:** Inter Variable (system-ui fallback)
**Body Font:** Inter Variable (system-ui fallback)
**Display Font (landing / marketing headings):** Source Serif 4 Variable — academic lab character on `.landing-h1` / `.landing-h2` only.
**Body Font:** Inter Variable (system-ui fallback) — product UI, body copy, controls.

**Character:** Clean, legible, technical-but-warm. Optimized for step descriptions and control labels at a glance.
**Character:** Clean, legible, technical-but-warm. Optimized for step descriptions and control labels at a glance. Marketing display adds a restrained serif for Clarity Lab presence without replacing Inter in the app.

### Hierarchy

- **Display** (700, clamp on landing heroes): Marketing headlines only.
- **Title** (600, 1.125–1.25rem): Panel titles, algorithm names.
- **Body** (400, 1rem, line-height 1.5): Descriptions, insight copy, settings.
- **Label** (600, 0.875rem, slight tracking): Control groups, tabs, chips.
- **Display** (700–800, clamp on landing heroes): Marketing headlines only (Source Serif 4).
- **Title** (600, 1.125–1.25rem): Panel titles, algorithm names (Inter).
- **Body** (400, 1rem, line-height 1.5): Descriptions, insight copy, settings (Inter).
- **Label** (600, 0.875rem, slight tracking): Control groups, tabs, chips (Inter).

### Named Rules

**The Inter Lock Rule.** Inter is the established brand face. Do not swap fonts unless the user explicitly requests a rebrand.
**The Inter Lock Rule.** Inter is the established brand face for product UI and body copy. Do not swap Inter app-wide unless the user explicitly requests a rebrand. **Exception:** landing/marketing display headings (`.landing-h1`, `.landing-h2`) may use Source Serif 4 Variable for academic-lab character.

## Elevation

Expand Down Expand Up @@ -180,5 +205,5 @@ Depth comes from tinted surface steps and restrained shadows, plus selective gla
- **Don't** nest cards inside cards in the visualizer shell; use spacing and dividers.
- **Don't** apply gradient text to headings or step descriptions.
- **Don't** use bounce or elastic easing on UI transitions.
- **Don't** swap Inter or replace Phosphor with emoji icons without an explicit rebrand request.
- **Don't** swap Inter app-wide or replace Phosphor with emoji icons without an explicit rebrand request (landing display serif on `.landing-h1` / `.landing-h2` is the documented exception).
- **Don't** add UI click sounds — audio is for visualization steps only.
2 changes: 1 addition & 1 deletion docs/AGENTS_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ Migrations: `20260710140000_pro_waitlist.sql`, `20260710150000_pro_waitlist_attr

### Landing and roadmap components

- `src/components/landing/` — `Hero`, `Features`, `AlgorithmTypes`, `ClaritySection`, `LearnYourWay`, `RoadmapCTA`, `TechPattern`
- `src/components/landing/` — `Hero`, `HeroVisualizerDemo`, `AlgorithmTypes`, `Features`, `ProPreview`, `ClaritySection`, `FAQ`, `RoadmapCTA`, `TechPattern`, `SocialProofStrip` (gated by `SHOW_LANDING_SOCIAL_PROOF`)
- `src/components/roadmap/` — `RoadmapHero`, `Timeline`, `TimelineItem`
- `src/components/ui/` — shared `Button`, `Container`, `Section`

Expand Down
6 changes: 4 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Bayan Flow is built as a single-page application (SPA) with multiple routes:
- No visualizer functionality

**Components:**
- Hero, LearnYourWay, AlgorithmTypes, Features, ClaritySection, RoadmapCTA, GitHubRepoBadge
- Hero, AlgorithmTypes, Features, ProPreview, ClaritySection, FAQ, RoadmapCTA, SocialProofStrip (gated), GitHubRepoBadge
- Footer, ThemeToggle, LanguageSwitcher
- TechPattern (animated background)

Expand Down Expand Up @@ -150,10 +150,12 @@ LandingPage
├── LanguageSwitcher
├── TechPattern (background)
├── Hero
├── LearnYourWay
├── SocialProofStrip (gated off)
├── AlgorithmTypes
├── Features
├── ProPreview
├── ClaritySection
├── FAQ
├── RoadmapCTA
└── Footer

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@dicebear/core": "^10.3.0",
"@dicebear/styles": "^10.2.0",
"@fontsource-variable/inter": "^5.2.8",
"@fontsource-variable/source-serif-4": "^5.3.0",
"@monaco-editor/react": "^4.7.0",
"@octokit/rest": "^22.0.1",
"@phosphor-icons/react": "^2.1.10",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/components/AlgorithmTipToast.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function AlgorithmTipToast({ algorithmKey, onClose }) {
};
}, [algorithmKey, onClose]);

const message = t(`algorithmUses.${algorithmKey}`, {
const message = t(`algorithmTips.${algorithmKey}`, {
defaultValue: '',
});

Expand Down
4 changes: 2 additions & 2 deletions src/components/AlgorithmTipToast.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ describe('AlgorithmTipToast', () => {
vi.useRealTimers();
});

it('renders the tip title and use-case message for the algorithm', () => {
it('renders the tip title and interview-focused message for the algorithm', () => {
renderWithI18n(
<AlgorithmTipToast algorithmKey="dijkstra" onClose={vi.fn()} />
);

expect(screen.getByText('Why it matters')).toBeInTheDocument();
expect(screen.getByText(/GPS routing/)).toBeInTheDocument();
expect(screen.getByText(/every navigation app/)).toBeInTheDocument();
});

it('exposes the message to assistive technology', () => {
Expand Down
89 changes: 89 additions & 0 deletions src/components/ArrayVisualizer.appDefaults.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Copyright (c) 2025 Bayan Flow
* Licensed under Elastic License 2.0 OR Commercial
* See LICENSE for details.
*/

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { act, renderWithProviders, screen } from '../test/testUtils';
import ArrayVisualizer from './ArrayVisualizer';
import i18n from '../i18n';

vi.mock('./ComplexityPanel', () => ({
default: () => <div data-testid="complexity-panel">Complexity</div>,
}));

vi.mock('./SwipeTutorial', () => ({
default: ({ show }) =>
show ? <div data-testid="swipe-tutorial">Swipe</div> : null,
}));

describe('ArrayVisualizer /app defaults', () => {
beforeEach(async () => {
localStorage.clear();
await i18n.changeLanguage('en');
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
});

const baseProps = {
array: [3, 1, 2],
states: ['default', 'comparing', 'default'],
description: 'algorithms.descriptions.bubbleSort',
algorithm: 'bubbleSort',
onStepForward: vi.fn(),
onStepBackward: vi.fn(),
mode: 'manual',
isComplete: false,
};

it('keeps legend, caption, and application role when demo props are omitted', () => {
renderWithProviders(<ArrayVisualizer {...baseProps} />);

// Legend auto-expands on mount; close control proves chrome is present
expect(screen.getByLabelText(i18n.t('legend.close'))).toBeInTheDocument();
expect(screen.getByRole('status')).toBeInTheDocument();
expect(
screen.getByRole('application', {
name: 'Array visualization - Swipe left/right to navigate steps',
})
).toBeInTheDocument();
});

it('still opens the complexity panel after completion', async () => {
renderWithProviders(<ArrayVisualizer {...baseProps} isComplete />);

await act(async () => {
vi.advanceTimersByTime(1000);
});

expect(screen.getByTestId('complexity-panel')).toBeInTheDocument();
});

it('hero demo flags hide legend and complexity while allowing caption', async () => {
renderWithProviders(
<ArrayVisualizer
{...baseProps}
mode="autoplay"
showChrome={false}
showCaption
interactive={false}
/>
);

expect(
screen.queryByLabelText(i18n.t('legend.show'))
).not.toBeInTheDocument();
expect(screen.getByRole('status')).toBeInTheDocument();
expect(screen.getByRole('img')).toBeInTheDocument();

await act(async () => {
vi.advanceTimersByTime(1000);
});

expect(screen.queryByTestId('complexity-panel')).not.toBeInTheDocument();
});
});
Loading
Loading