Apply Butter & Maple to homepage, login, docs, workspace#179
Conversation
Why: the design handoff in ~/Downloads/design_handoff_wafflebase_landing locked a cream-and-syrup palette (Fraunces + Inter + JetBrains Mono, ruler-motif backdrop) for the landing page. Apply it consistently so the unauthenticated path (/, /login, /docs) and the authenticated workspace shell share one visual identity. Scope: - Homepage: full rebuild around the new tokens — sticky NavBar, focused single-column hero with ruler backdrop, tabbed live-demo card with spreadsheet + word-processor iframes (lazy-mounted), 3+4 feature grid with waffle-pocket SVG glyphs, Use Cases section, restyled Why comparison, dark dev-tools card with REST/CLI tabs, Open Source CTA with BigWaffle illustration, restyled Footer. - Login: new header layout matching NavBar geometry, paper card with "Welcome back" Fraunces heading, syrup pill GitHub CTA. - Docs (VitePress): map --vp-c-* tokens to Butter & Maple, load Fraunces for headings. - Workspace shell: remap shadcn --primary / --ring / --sidebar-* to the Butter & Maple tokens so the same syrup pill, butter active-nav glow, and cream sidebar reach Documents, Data Sources, Settings, Sheet, and Doc routes. Swap the sidebar Grid2x2Plus icon for the WaffleLogo primitive. - Sheet engine theme: replace amber active-cell ring + header chip with syrup + butter, swap two yellow peer-cursor slots for rose / lavender so collaborator labels stop competing with the system accent. - Drop deprecated --homepage-* tokens (no remaining callers). Verification: pnpm verify:self passes (8 lanes, ~63s) — sheets / docs / frontend / backend / cli builds, verify:fast (40 files, 686 tests, lint 0), chunk gate, and entropy checks all green. Frontend chunk count limit bumped 70 → 80 to absorb the new primitives + section. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (76)
📒 Files selected for processing (4)
📝 WalkthroughWalkthroughImplements the "Butter & Maple" design system and applies it across the homepage: new CSS tokens, typography, primitives, SVG assets, redesigned sections (demo, hero, features, developer, use-cases, why, OSS, footer, nav), a two-tab lazy iframe demo with theme postMessage sync, and related docs/config updates. ChangesButter & Maple design system & homepage redesign
Sequence DiagramsequenceDiagram
participant User
participant DemoSection
participant SheetIframe as Sheet Iframe
participant DocIframe as Doc Iframe
participant ThemeProvider
User->>DemoSection: Click "Word processor" tab
DemoSection->>DemoSection: Check docIframe mounted?
alt not mounted
DemoSection->>DocIframe: Mount iframe (lazy)
DocIframe->>DocIframe: Load & initialize
end
DemoSection->>SheetIframe: Toggle visibility (hide)
DemoSection->>DocIframe: Toggle visibility (show)
User->>ThemeProvider: Toggle theme
ThemeProvider->>DemoSection: Notify theme change
DemoSection->>SheetIframe: postMessage({ type: 'theme-change', theme })
DemoSection->>DocIframe: postMessage({ type: 'theme-change', theme })
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 27 minutes and 48 seconds.Comment |
Verification: verify:selfResult: ✅ PASS in 129.9s
Verification: verify:integrationResult: ✅ PASS |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (5)
harness.config.json (1)
5-5: ⚡ Quick winVerify remaining headroom against the new limit.
The limit has now been raised twice (60 hard default → 70 → 80). The gate's value as a regression guard depends on how tightly it tracks the actual count. If the build already lands at, e.g., 78–79 chunks, the next incremental change will trip it again. Knowing the exact post-build count (and baking it + a small buffer into the comment or a
reasonfield here) would make future bumps more deliberate.Run the following after a local
pnpm buildto see the real count and remaining headroom:#!/bin/bash # Description: Count .js chunks in the frontend build output and compare against the configured limit. # Expects the frontend dist/assets directory to exist after a build. ASSETS_DIR=$(fd -t d "assets" --search-path . | head -n 1) if [ -z "$ASSETS_DIR" ]; then echo "Could not locate an assets directory. Run pnpm build first." exit 1 fi COUNT=$(find "$ASSETS_DIR" -maxdepth 1 -name "*.js" -type f | wc -l) LIMIT=$(python3 -c "import json,sys; print(json.load(open('harness.config.json'))['frontend']['chunkBudgets']['maxChunkCount'])") echo "Actual JS chunk count : $COUNT" echo "Configured limit : $LIMIT" echo "Remaining headroom : $((LIMIT - COUNT))"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@harness.config.json` at line 5, Update the harness.config.json frontend.chunkBudgets.maxChunkCount entry to include a documented reason and realistic headroom: run a local pnpm build, count the produced .js chunks (frontend assets) and record the measured count plus a small buffer in a new "reason" field next to "maxChunkCount" (or in a comment in the file metadata if comments are supported); ensure the value of maxChunkCount (currently 80) is set to that measured count plus buffer so the gate has intentional headroom and include the exact measured count and chosen buffer in the reason for future reviewers.packages/frontend/src/app/home/primitives/feature-glyph.tsx (1)
1-1: 💤 Low valueConsider exporting
GlyphKindfor downstream type safety.
GlyphKindis used in the exported component's prop but is not itself exported. Consumers that want to type their feature data arrays — likefeatures-section.tsx— must useReact.ComponentProps<typeof FeatureGlyph>['kind']instead of the more readableGlyphKind.♻️ One-word fix
-type GlyphKind = "reactive" | "formulas" | "embed" | "sync" | "io"; +export type GlyphKind = "reactive" | "formulas" | "embed" | "sync" | "io";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/app/home/primitives/feature-glyph.tsx` at line 1, The type alias GlyphKind is currently internal but used in the exported FeatureGlyph props; export it so downstream consumers can import GlyphKind for typing instead of deriving it via ComponentProps. Modify the declaration of GlyphKind (the type alias named GlyphKind) to be exported (e.g., export type GlyphKind = ...) and update any index/re-export barrels if this file's types are re-exported elsewhere so consumers like features-section.tsx can import GlyphKind directly.packages/frontend/src/app/home/primitives/ruler-backdrop.tsx (1)
28-113: ⚡ Quick winHardcoded SVG
idwill collide if twoRulerBackdropinstances appear in the same document.
id="wb-ruler-pat"is a global DOM identifier. Although the two current call sites (hero-section.tsxandlogin/page.tsx) are on separate routes, a second instance in the same document (e.g., added to a shared layout or a multi-section page) would cause bothfill="url(#wb-ruler-pat)"references to resolve against whichever<pattern>the browser encounters first, potentially applying the wrong tile dimensions. UseuseId()(React 18+, available in this project's React 19 baseline) to generate a per-instance unique ID.🔧 Proposed fix
-import { cn } from "@/lib/utils"; +import { useId } from "react"; +import { cn } from "@/lib/utils"; export function RulerBackdrop({ className }: RulerBackdropProps) { + const patternId = useId(); return ( <div ... > <svg width="100%" height="100%"> <defs> <pattern - id="wb-ruler-pat" + id={patternId} ... > ... </pattern> </defs> - <rect width="100%" height="100%" fill="url(`#wb-ruler-pat`)" /> + <rect width="100%" height="100%" fill={`url(#${patternId})`} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/app/home/primitives/ruler-backdrop.tsx` around lines 28 - 113, Replace the hardcoded SVG id "wb-ruler-pat" with a per-instance unique id using React's useId(); inside the RulerBackdrop component (function RulerBackdrop) call useId(), build a patternId (e.g., `${id}-wb-ruler-pat`), set the <pattern id={patternId}> and update every reference to that id (the rect fill="url(#...)" and any other url(...) usages) to use the generated id string; also add the import for useId from 'react' if missing.packages/frontend/src/components/login-form.tsx (1)
3-3: 💤 Low value
WbButtonis used outsideapp/home/— consider relocating it tosrc/components/.
login-form.tsxlives insrc/components/(shared layer) but imports fromsrc/app/home/primitives/(page layer). Now thatWbButtonis consumed by a non-homepage route, it effectively belongs in the shared components layer alongside other design-system primitives.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/components/login-form.tsx` at line 3, WbButton is a design primitive imported from app/home/primitives into a shared component (login-form.tsx); move the WbButton component file (identified as WbButton) from app/home/primitives into the shared components directory (e.g., src/components/ or the existing design-system primitives folder), update its module export if needed, then update all imports that reference app/home/primitives/WbButton (including in login-form.tsx) to the new shared path so the primitive is accessible from non-page code.packages/frontend/src/app/home/developer-section.tsx (1)
197-240: ⚡ Quick winMissing ARIA tab pattern degrades screen-reader navigation.
The tab strip and code panel have no semantic tab role structure. Screen readers will announce them as plain buttons with no indication that they control the panel below.
♿ Proposed fix — add ARIA tab roles
<div - className="flex items-center px-2 border-b" + role="tablist" + aria-label="Developer API examples" + className="flex items-center px-2 border-b" style={{ ... }} > {TABS.map((t) => ( <button key={t.key} type="button" + role="tab" + id={`tab-${t.key}`} + aria-selected={tab === t.key} + aria-controls={`panel-${t.key}`} onClick={() => setTab(t.key)} ... >- <pre className="m-0 px-6 md:px-8 py-7 overflow-x-auto font-code text-[14px] leading-7 whitespace-pre"> + <pre + role="tabpanel" + id={`panel-${active.key}`} + aria-labelledby={`tab-${active.key}`} + className="m-0 px-6 md:px-8 py-7 overflow-x-auto font-code text-[14px] leading-7 whitespace-pre" + >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/app/home/developer-section.tsx` around lines 197 - 240, The tab strip and code panel need ARIA tab pattern markup: wrap the TABS.map container div with role="tablist" (preserving its classes), render each button (the one using key t.key, onClick setTab and checking tab === t.key) with role="tab", an id like `tab-${t.key}`, aria-selected set to (tab === t.key), aria-controls pointing to a matching panel id `panel-${t.key}`, and include keyboard focusability as needed; then mark the code container (the pre that renders highlightCode(active.code)) as role="tabpanel" with id `panel-${active.key}` and aria-labelledby pointing to `tab-${active.key}`, and set hidden (or aria-hidden) when it is not the active panel to ensure screen readers know which tab is active.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design/homepage.md`:
- Line 225: The fenced code block showing the file structure (the triple
backticks around the "packages/frontend/src/app/home/ ├── page.tsx ..." snippet)
lacks a language identifier; update that opening fence to include a language
specifier such as ```text or ```plain so the block becomes ```text (or ```plain)
to satisfy markdownlint MD040 and avoid the warning.
- Line 88: Update the summary table entry for DemoSection to match the detailed
spec: replace the phrase "Doc tab renders `<DocPreview>`" with wording that
indicates the Doc tab is a live iframe serving
/shared/{VITE_DEMO_DOC_SHARED_TOKEN} (or similar live iframe token URL). Locate
the DemoSection mention in the summary row (reference symbol: DemoSection) and
ensure the text references the live iframe endpoint instead of DocPreview so the
table aligns with the detailed spec and revision `#3`.
In `@docs/tasks/archive/2026/05/20260503-homepage-redesign-todo.md`:
- Around line 9-11: The document contains a committed macOS home-directory path
string (the design handoff directory name) that leaks a username; open
docs/tasks/archive/2026/05/20260503-homepage-redesign-todo.md and replace the
absolute path occurrences with a neutral project-relative reference or short
descriptive note (e.g., "design_handoff_wafflebase_landing in project assets" or
"see design handoff bundle in /designs/") so the username is not recorded;
update both instances of the referenced design handoff mention and keep the note
clear about where contributors can find the design within the repo.
In `@packages/documentation/.vitepress/theme/style.css`:
- Around line 55-58: Remove unnecessary quotes from single-word font family
names and suppress the false-positive keyword-case lint errors: update the
font-family declarations referenced by the CSS custom properties (e.g.,
--vp-font-family-mono and the other --vp-font-family-... declarations that
include "Inter" and "Fraunces") to use unquoted single-word names (Inter,
Fraunces) and either add a preceding /* stylelint-disable-next-line
value-keyword-case */ comment for the affected font-family lines or adjust the
stylelint config to ignore font-family values so proprietary family names like
Roboto, Arial, SFMono-Regular, Menlo, Monaco, and Consolas are not forced to
lowercase.
In `@packages/frontend/src/app/home/demo-section.tsx`:
- Around line 29-30: The current code recomputes sheetUrl and docUrl on every
render using resolvedTheme, causing iframe src changes and full reloads; fix
this by initializing two state variables (e.g., initialSheetUrl and
initialDocUrl) with a lazy useState initializer that builds the URLs once from
the initial resolvedTheme, then use those state values as the iframe src props
instead of sheetUrl/docUrl; continue to send theme changes via the existing
postMessage path (do not change that logic) so the iframe updates without
resetting its src.
In `@packages/frontend/src/app/home/developer-section.tsx`:
- Around line 191-202: The container and tab-bar in developer-section.tsx are
using the foreground token --wb-ink as a background which inverts colors in dark
mode; add terminal-specific CSS tokens (--wb-terminal-bg and --wb-terminal-fg)
in index.css (with stable dark values under .dark) and replace usages in
developer-section.tsx (the outer container style and the inline tab-bar
background) to use --wb-terminal-bg/--wb-terminal-fg; also update TOKEN_CLASSES
(or any places deriving text colors for the terminal) to derive from
--wb-terminal-fg instead of --wb-paper so syntax tokens and text keep proper
contrast in both light and dark modes.
In `@packages/frontend/src/app/home/features-section.tsx`:
- Line 6: Remove the duplicate GlyphKind definition in features-section.tsx and
instead import the exported type from the feature-glyph component; add an export
of the GlyphKind type in FeatureGlyph (feature-glyph.tsx) if it's not already
exported, then update HeroFeature.glyph and any local usages in
features-section.tsx to reference the imported GlyphKind type so all components
(FeatureGlyph and HeroFeature) share a single source of truth.
In `@packages/frontend/src/app/home/primitives/big-waffle.tsx`:
- Around line 55-75: The SVG defs in BigWaffle use fixed ids (wb-waffle-body,
wb-pocket-shade, wb-butter-face, wb-soft-shadow) which will collide across
multiple mounts; fix this by generating a per-instance prefix via React's
useId() inside the BigWaffle component and append it to each def id and every
reference to them (e.g., url(`#wb-waffle-body-`{id}), url(`#wb-pocket-shade-`{id}),
url(`#wb-butter-face-`{id}), url(`#wb-soft-shadow-`{id})); update all places that
reference these ids (fill, filter, xlink:href or other url() usages) so each
rendered SVG uses the unique ids.
In `@packages/frontend/src/app/home/primitives/wb-button.tsx`:
- Around line 41-47: The WbButton currently renders a plain <button> (Comp)
without a default type, causing unintended form submissions; update the
component (wb-button.tsx, around the Comp render) to ensure it supplies
type="button" for the rendered button element unless the consumer explicitly
provides a type or asChild is true (i.e., when Comp === Slot do not inject
type). Implement this by preferring an incoming props.type if present, otherwise
setting type="button" when rendering the native button.
In `@packages/frontend/src/index.css`:
- Around line 52-54: The stylelint `value-keyword-case` rule is falsely flagging
font-family names in the CSS vars (--font-display, --font-body, --font-code);
suppress the rule around these declarations by adding a stylelint disable/enable
comment (e.g. place a /* stylelint-disable-next-line value-keyword-case */
immediately before each problematic variable or wrap the `@theme` inline block
with /* stylelint-disable value-keyword-case */ and then /* stylelint-enable
value-keyword-case */ after) so the font-family names (Fraunces, Georgia, Inter,
SFMono-Regular, Menlo, JetBrains Mono) are allowed without changing the values.
---
Nitpick comments:
In `@harness.config.json`:
- Line 5: Update the harness.config.json frontend.chunkBudgets.maxChunkCount
entry to include a documented reason and realistic headroom: run a local pnpm
build, count the produced .js chunks (frontend assets) and record the measured
count plus a small buffer in a new "reason" field next to "maxChunkCount" (or in
a comment in the file metadata if comments are supported); ensure the value of
maxChunkCount (currently 80) is set to that measured count plus buffer so the
gate has intentional headroom and include the exact measured count and chosen
buffer in the reason for future reviewers.
In `@packages/frontend/src/app/home/developer-section.tsx`:
- Around line 197-240: The tab strip and code panel need ARIA tab pattern
markup: wrap the TABS.map container div with role="tablist" (preserving its
classes), render each button (the one using key t.key, onClick setTab and
checking tab === t.key) with role="tab", an id like `tab-${t.key}`,
aria-selected set to (tab === t.key), aria-controls pointing to a matching panel
id `panel-${t.key}`, and include keyboard focusability as needed; then mark the
code container (the pre that renders highlightCode(active.code)) as
role="tabpanel" with id `panel-${active.key}` and aria-labelledby pointing to
`tab-${active.key}`, and set hidden (or aria-hidden) when it is not the active
panel to ensure screen readers know which tab is active.
In `@packages/frontend/src/app/home/primitives/feature-glyph.tsx`:
- Line 1: The type alias GlyphKind is currently internal but used in the
exported FeatureGlyph props; export it so downstream consumers can import
GlyphKind for typing instead of deriving it via ComponentProps. Modify the
declaration of GlyphKind (the type alias named GlyphKind) to be exported (e.g.,
export type GlyphKind = ...) and update any index/re-export barrels if this
file's types are re-exported elsewhere so consumers like features-section.tsx
can import GlyphKind directly.
In `@packages/frontend/src/app/home/primitives/ruler-backdrop.tsx`:
- Around line 28-113: Replace the hardcoded SVG id "wb-ruler-pat" with a
per-instance unique id using React's useId(); inside the RulerBackdrop component
(function RulerBackdrop) call useId(), build a patternId (e.g.,
`${id}-wb-ruler-pat`), set the <pattern id={patternId}> and update every
reference to that id (the rect fill="url(#...)" and any other url(...) usages)
to use the generated id string; also add the import for useId from 'react' if
missing.
In `@packages/frontend/src/components/login-form.tsx`:
- Line 3: WbButton is a design primitive imported from app/home/primitives into
a shared component (login-form.tsx); move the WbButton component file
(identified as WbButton) from app/home/primitives into the shared components
directory (e.g., src/components/ or the existing design-system primitives
folder), update its module export if needed, then update all imports that
reference app/home/primitives/WbButton (including in login-form.tsx) to the new
shared path so the primitive is accessible from non-page code.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9253e9fa-9c45-4357-a531-841bc4aa4d92
📒 Files selected for processing (30)
docs/design/homepage.mddocs/tasks/README.mddocs/tasks/archive/2026/05/20260503-homepage-redesign-todo.mddocs/tasks/archive/README.mdharness.config.jsonpackages/documentation/.vitepress/config.tspackages/documentation/.vitepress/theme/style.csspackages/frontend/index.htmlpackages/frontend/src/app/home/demo-section.tsxpackages/frontend/src/app/home/developer-section.tsxpackages/frontend/src/app/home/features-section.tsxpackages/frontend/src/app/home/footer.tsxpackages/frontend/src/app/home/hero-section.tsxpackages/frontend/src/app/home/nav-bar.tsxpackages/frontend/src/app/home/opensource-section.tsxpackages/frontend/src/app/home/page.tsxpackages/frontend/src/app/home/primitives/big-waffle.tsxpackages/frontend/src/app/home/primitives/feature-glyph.tsxpackages/frontend/src/app/home/primitives/ruler-backdrop.tsxpackages/frontend/src/app/home/primitives/section-head.tsxpackages/frontend/src/app/home/primitives/theme-toggle.tsxpackages/frontend/src/app/home/primitives/waffle-logo.tsxpackages/frontend/src/app/home/primitives/wb-button.tsxpackages/frontend/src/app/home/use-cases-section.tsxpackages/frontend/src/app/home/why-section.tsxpackages/frontend/src/app/login/page.tsxpackages/frontend/src/components/app-sidebar.tsxpackages/frontend/src/components/login-form.tsxpackages/frontend/src/index.csspackages/sheets/src/view/theme.ts
| "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, | ||
| "Helvetica Neue", Arial, sans-serif; | ||
| --vp-font-family-mono: | ||
| "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, |
There was a problem hiding this comment.
Stylelint reports errors on font-family declarations.
The static analysis tool flags two distinct rule violations:
-
font-family-name-quotes(line 111):"Fraunces"is a single-word family name; the project's stylelint config expects it unquoted (Fraunces). Single-word names like"Inter"inindex.cssline 53 have the same issue. -
value-keyword-case(lines 55–58):Roboto,Arial,SFMono-Regular,Menlo,Monaco, andConsolasare flagged as requiring lowercase. These are proprietary font-family names rather than CSS keywords, so these are likely false positives from an overly broad rule configuration, but they are still reported as errors by stylelint.
CI currently passes (the docs CSS may not be in the lint pipeline), but these will block if linting scope is extended.
🛠️ Proposed fix for the quotes violation
.vp-doc h1,
.vp-doc h2,
.vp-doc h3,
.vp-doc h4,
.VPHero .name,
.VPHero .text {
font-family:
- "Fraunces", ui-serif, Georgia, "Times New Roman", Times, serif;
+ Fraunces, ui-serif, Georgia, "Times New Roman", Times, serif;
font-feature-settings: "ss01" on, "ss02" on;
letter-spacing: -0.01em;
}For the value-keyword-case false positives, the cleanest resolution is to add a /* stylelint-disable-next-line value-keyword-case */ comment before each affected declaration, or configure the rule to ignore font-family values in the stylelint config.
Also applies to: 110-113
🧰 Tools
🪛 Stylelint (17.9.0)
[error] 55-55: Expected "Roboto" to be "roboto" (value-keyword-case)
(value-keyword-case)
[error] 56-56: Expected "Arial" to be "arial" (value-keyword-case)
(value-keyword-case)
[error] 58-58: Expected "SFMono-Regular" to be "sfmono-regular" (value-keyword-case)
(value-keyword-case)
[error] 58-58: Expected "Menlo" to be "menlo" (value-keyword-case)
(value-keyword-case)
[error] 58-58: Expected "Monaco" to be "monaco" (value-keyword-case)
(value-keyword-case)
[error] 58-58: Expected "Consolas" to be "consolas" (value-keyword-case)
(value-keyword-case)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/documentation/.vitepress/theme/style.css` around lines 55 - 58,
Remove unnecessary quotes from single-word font family names and suppress the
false-positive keyword-case lint errors: update the font-family declarations
referenced by the CSS custom properties (e.g., --vp-font-family-mono and the
other --vp-font-family-... declarations that include "Inter" and "Fraunces") to
use unquoted single-word names (Inter, Fraunces) and either add a preceding /*
stylelint-disable-next-line value-keyword-case */ comment for the affected
font-family lines or adjust the stylelint config to ignore font-family values so
proprietary family names like Roboto, Arial, SFMono-Regular, Menlo, Monaco, and
Consolas are not forced to lowercase.
Why: CodeRabbit flagged a few real bugs and accessibility gaps in the homepage redesign. Folding the fixes here so the PR is mergeable. - WbButton now defaults `type="button"` for the native-button path so it never accidentally submits a parent form. asChild mode still defers to the underlying element's own type. - DemoSection iframe URLs are now lazy-init via useState so theme changes no longer mutate the `src` prop and force a full reload — theme updates flow through postMessage as intended. - DeveloperSection swapped from `--wb-ink` (which inverts to cream in dark mode) to dedicated `--wb-terminal-bg` / `--wb-terminal-fg` tokens that stay locked across themes; the dark code card finally renders dark in both light and dark. - DeveloperSection tabs got the proper ARIA tablist / tab / tabpanel roles + tabindex roving so screen readers announce the relationship. - RulerBackdrop and BigWaffle now derive SVG `id` values via React's `useId()` so multiple instances on the same document can't collide. - FeatureGlyph exports `GlyphKind`; FeaturesSection imports it instead of redeclaring the union (single source of truth). - harness.config.json captures the chunk-count bump rationale next to the limit, with the measured count + buffer for future reviewers. - Sanitized the archived task doc — replaced the absolute `/Users/<name>/Downloads/...` handoff path with a neutral reference to the shared bundle. - Design doc table row for DemoSection no longer says "DocPreview" — both tabs are live iframes now. Verification: pnpm verify:self passes (8 lanes, ~65s). Visual baselines left untouched here — they need a Docker (linux/amd64) refresh, which the next commit / CI artifact will produce. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
packages/frontend/src/index.css (1)
52-54:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winStylelint
value-keyword-caseerrors on font-family names remain unresolved.
Georgia,SFMono-Regular, andMenloare font-family names, not CSS keywords, so the errors are false positives — but they are still reported as[error]by stylelint and will break a lint gate. Adding inline disable/enable comments around the affected declarations is the standard suppression approach.✏️ Proposed fix
+ /* stylelint-disable value-keyword-case */ --font-display: "Fraunces", ui-serif, Georgia, serif; --font-body: "Inter", ui-sans-serif, system-ui, sans-serif; --font-code: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; + /* stylelint-enable value-keyword-case */🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/index.css` around lines 52 - 54, The stylelint false positives for font-family names are coming from the CSS custom properties --font-display, --font-body, and --font-code in index.css; wrap those specific declarations with inline stylelint suppression comments to disable and then re-enable the value-keyword-case rule (i.e., add a /* stylelint-disable value-keyword-case */ immediately before the three --font-... declarations and a matching /* stylelint-enable value-keyword-case */ immediately after) so linting ignores these font-family identifiers while leaving the rest of the file checked.
🧹 Nitpick comments (1)
packages/frontend/src/app/home/developer-section.tsx (1)
211-240: ⚡ Quick winArrow key navigation missing from the
role="tablist"— the roving tabindex pattern is only half-implemented.The tabIndex values are correctly set (0 for active, -1 for inactive), but without an
onKeyDownhandler forArrowRight/ArrowLeft, keyboard users cannot cycle between tabs using the arrow keys. The WAI-ARIA Tab Panel pattern requires this. Instead they'd have to Tab out and back in, which breaks expected tab-widget behavior.♻️ Proposed addition
Add a shared handler and pass it to each tab button:
+ const handleTabKeyDown = (e: React.KeyboardEvent, currentKey: TabKey) => { + const idx = TABS.findIndex((t) => t.key === currentKey); + let next: typeof TABS[number] | undefined; + if (e.key === "ArrowRight") next = TABS[(idx + 1) % TABS.length]; + if (e.key === "ArrowLeft") next = TABS[(idx - 1 + TABS.length) % TABS.length]; + if (next) { + setTab(next.key); + // Move DOM focus to the newly active tab button + document.getElementById(`dev-tab-${next.key}`)?.focus(); + } + }; {TABS.map((t) => ( <button key={t.key} type="button" role="tab" id={`dev-tab-${t.key}`} aria-selected={tab === t.key} aria-controls={`dev-panel-${t.key}`} tabIndex={tab === t.key ? 0 : -1} onClick={() => setTab(t.key)} + onKeyDown={(e) => handleTabKeyDown(e, t.key)} className={...} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/app/home/developer-section.tsx` around lines 211 - 240, The tablist is missing ArrowLeft/ArrowRight keyboard support; implement an onKeyDown handler (e.g., handleTabKeyDown) that listens for ArrowRight and ArrowLeft, computes the next/previous index within TABS (wrapping at ends), calls setTab(TABS[newIndex].key) and moves focus to the corresponding button (use the button ids like `dev-tab-${key}` or refs), and attach this handler to each tab button (the elements using role="tab" and tabIndex logic). Ensure the handler prevents default for arrow keys and preserves the existing aria-selected/tabIndex behavior so keyboard users can rove focus correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design/homepage.md`:
- Around line 257-259: The risk table incorrectly claims a static screenshot
fallback exists; update the table entry to reflect the real behavior (the
DemoFrame component renders an inline error message "Demo unavailable. Try
refreshing the page.") or alternatively implement the documented fallback by
adding a static screenshot under public/images and wiring DemoFrame to use it on
iframe errors; reference the DemoFrame component when making the change so the
doc and implementation stay consistent.
In `@packages/frontend/src/app/home/demo-section.tsx`:
- Around line 74-94: The tab UI lacks ARIA tab semantics: update the DemoSection
container div to include role="tablist" and generate stable IDs for tabs/panels;
modify DemoTab (and its type DemoTabProps) to accept id, panelId, role="tab",
aria-selected (true for the active tab), aria-controls (panelId) and a roving
tabIndex (0 for active, -1 otherwise) and ensure onClick/focus set active;
update DemoFrameProps and the DemoFrame outer div to role="tabpanel", id
(panelId) and aria-labelledby (tab id) and pass the new id/panelId props from
DemoSection when rendering DemoTab and DemoFrame so tabs and panels are properly
linked.
---
Duplicate comments:
In `@packages/frontend/src/index.css`:
- Around line 52-54: The stylelint false positives for font-family names are
coming from the CSS custom properties --font-display, --font-body, and
--font-code in index.css; wrap those specific declarations with inline stylelint
suppression comments to disable and then re-enable the value-keyword-case rule
(i.e., add a /* stylelint-disable value-keyword-case */ immediately before the
three --font-... declarations and a matching /* stylelint-enable
value-keyword-case */ immediately after) so linting ignores these font-family
identifiers while leaving the rest of the file checked.
---
Nitpick comments:
In `@packages/frontend/src/app/home/developer-section.tsx`:
- Around line 211-240: The tablist is missing ArrowLeft/ArrowRight keyboard
support; implement an onKeyDown handler (e.g., handleTabKeyDown) that listens
for ArrowRight and ArrowLeft, computes the next/previous index within TABS
(wrapping at ends), calls setTab(TABS[newIndex].key) and moves focus to the
corresponding button (use the button ids like `dev-tab-${key}` or refs), and
attach this handler to each tab button (the elements using role="tab" and
tabIndex logic). Ensure the handler prevents default for arrow keys and
preserves the existing aria-selected/tabIndex behavior so keyboard users can
rove focus correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bf6b3541-550a-41ae-8cb3-7338e15aaa15
📒 Files selected for processing (11)
docs/design/homepage.mddocs/tasks/archive/2026/05/20260503-homepage-redesign-todo.mdharness.config.jsonpackages/frontend/src/app/home/demo-section.tsxpackages/frontend/src/app/home/developer-section.tsxpackages/frontend/src/app/home/features-section.tsxpackages/frontend/src/app/home/primitives/big-waffle.tsxpackages/frontend/src/app/home/primitives/feature-glyph.tsxpackages/frontend/src/app/home/primitives/ruler-backdrop.tsxpackages/frontend/src/app/home/primitives/wb-button.tsxpackages/frontend/src/index.css
✅ Files skipped from review due to trivial changes (1)
- harness.config.json
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/frontend/src/app/home/primitives/feature-glyph.tsx
- packages/frontend/src/app/home/primitives/big-waffle.tsx
Why: the Butter & Maple migration recolored the sheet engine — active cell ring (amber → syrup), header chip (amber → butter), and two peer-cursor slots (yellow → rose / lavender). Every browser visual baseline that touches those pixels needs a refresh. Source: CI workflow run 25276627500's `browser-visual-actual` artifact (linux/amd64, Docker). Baselines must come from CI's environment, not a local Apple Silicon machine, so the comparison stays pixel-stable. 76 actual.png files downloaded; 75 differed from existing baselines. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Why: CodeRabbit's follow-up flagged a few accessibility + lint loose ends from the first review pass. Tightening them so the PR doesn't carry forward known gaps. - DemoSection tabs now expose the full WAI-ARIA tab pattern: outer `role="tablist"`, each `<DemoTab>` renders `role="tab"` with id / aria-selected / aria-controls / roving tabindex, and `<DemoFrame>` renders `role="tabpanel"` with aria-labelledby + hidden. ArrowLeft / ArrowRight cycle focus between tabs (with wrap-around). - DeveloperSection tabs got the same ArrowLeft / ArrowRight handler so the tablist/tab semantics added last round can actually be navigated by keyboard alone. - index.css wraps the three `--font-*` declarations with `stylelint-disable value-keyword-case` so the false positives on Georgia / SFMono-Regular / Menlo stop blocking the lint gate. - homepage.md risk row no longer claims a static-screenshot iframe fallback; it now describes the actual behavior — `<DemoFrame>` swaps to an inline "Demo unavailable" message on iframe error. Verification: pnpm verify:self passes (8 lanes, ~60s). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Why: Wafflebase Docs is a word processor, not a formula-bearing doc, so
the hint promised behavior the iframe doesn't actually have. The phrase
was a holdover from the design prototype's static DocPreview, which
demonstrated inline ƒ formula tokens — the live iframe doesn't render
those. Replaced with a copy that matches what users will actually see
("changes sync in real time").
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Summary
/) around the locked Butter & Maple design(cream-and-syrup palette, Fraunces + Inter + JetBrains Mono, ruler-motif
backdrop): sticky NavBar, single-column hero, tabbed live-demo card with
spreadsheet + word-processor iframes (lazy-mounted), feature grid with
waffle-pocket SVG glyphs, Use Cases / Why / dev-tools / Open Source CTA
with BigWaffle illustration / Footer.
/login,/docs(VitePress), and theauthenticated workspace shell so
/,/login,/docs,/w/...,Sheet (
/s/...) and Doc (/d/...) routes share one visual language.--primary/--ring/--sidebar-*to the new tokens soevery default
<Button>becomes a syrup pill and the workspace sidebargoes cream + WaffleLogo + Fraunces wordmark.
yellow peer-cursor slots replaced with rose / lavender so collaborator
labels stop clashing with the syrup-amber accent.
--homepage-*tokens (no remaining callers).Test plan
pnpm verify:selfpasses locally (8 lanes — already green at thetip commit, ~63s).
http://localhost:5173/in light + dark; confirm the heroruler backdrop fades smoothly with no rectangular box artifact.
confirm both iframes render with theme sync (no reload).
/login, confirm logo lines up at the same x-position as thehome NavBar (both
left=48pxon a 1280px viewport)./docs, confirm h1–h4 use Fraunces and brand color is syrup.+ Newis a syrup pill, the active nav item has the butter glow.Toggle dark mode; verify continuity across pages.
column/row header is butter, and the
Sheet1tab underline issyrup. Repeat in dark mode (ring → maple amber).
collaborator joined, confirm peer labels are no longer bright
yellow (now rose / lavender / one of the existing distinct hues).
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Style