feat(website): add tabbed code snippet section to homepage#4084
feat(website): add tabbed code snippet section to homepage#4084
Conversation
Replace HomepageShowcases with a new interactive tabbed code panel inspired by the Node.js homepage. Features 5 Scala code examples with syntax highlighting, line numbers, copy-to-clipboard, and responsive two-column layout. Section alternates background color with adjacent sections following the homepage design pattern. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
✅ Deploy Preview for zio-http ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
…ating color pattern Change background from light gray to white to match the homepage's alternating white-gray-white pattern starting after the hero banner. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ections Change HomepageEcosystem and HomepageUsers to white background to create proper alternating pattern: white → gray → white → gray → white across all main sections. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…light mode contrast Change gray sections from var(--ifm-color-emphasis-0) to var(--ifm-color-emphasis-100) to ensure proper visual distinction in light mode while maintaining contrast in dark mode. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new interactive, tabbed “ZIO HTTP in Action” code-snippet section to the Docusaurus homepage, replacing the older HomepageShowcases section to provide a more engaging, copyable set of Scala examples.
Changes:
- Replaces
HomepageShowcaseswith a newHomepageCodeSnippetsection on the homepage. - Introduces
HomepageCodeSnippetReact component with tab switching, Prism highlighting, and copy-to-clipboard feedback. - Adds dedicated CSS module styling for a responsive two-column layout and dark code panel UI.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| website/src/pages/index.js | Swaps the homepage section from HomepageShowcases to HomepageCodeSnippet. |
| website/src/components/HomepageCodeSnippet/index.js | New tabbed code example component with syntax highlighting and copy button. |
| website/src/components/HomepageCodeSnippet/styles.module.css | Styles for layout, tab bar, code panel, line numbers, and responsive behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .codeSnippetSection { | ||
| background-color: var(--ifm-background-surface-color); | ||
| padding: 5rem 0; | ||
| width: 100%; |
There was a problem hiding this comment.
The section background color doesn’t match the PR description (“Alternating backgrounds: uses var(--ifm-color-emphasis-100)”). The CSS currently uses var(--ifm-background-surface-color), which likely won’t create an alternating emphasis band. Either switch to var(--ifm-color-emphasis-100) (or the intended token) or update the PR description to reflect the actual styling.
| .pre { | ||
| margin: 0; | ||
| padding: 1.25rem 1rem; | ||
| overflow-y: auto; | ||
| max-height: 500px; | ||
| font-size: 0.875rem; | ||
| line-height: 1.6; | ||
| } |
There was a problem hiding this comment.
.pre sets overflow-y: auto but not overflow-x. Since .codePanel has overflow: hidden, long code lines can be clipped with no horizontal scroll. Consider using overflow: auto (or at least overflow-x: auto) on .pre to ensure long lines remain accessible.
There was a problem hiding this comment.
✅ Fixed in 7daeaae — changed overflow-y: auto to overflow: auto to enable both vertical and horizontal scrolling
| navigator.clipboard | ||
| .writeText(TABS[activeTab].code.trim()) | ||
| .then(() => { | ||
| setCopied(true); | ||
| setTimeout(() => setCopied(false), 2000); | ||
| }) | ||
| .catch((err) => { | ||
| console.error('Failed to copy:', err); | ||
| }); |
There was a problem hiding this comment.
navigator.clipboard is not guaranteed to exist in all browsers/contexts (e.g., non-secure contexts or restricted permissions). Accessing navigator.clipboard.writeText(...) can throw synchronously before the Promise chain, so the .catch(...) won’t run. Add a guard for navigator.clipboard?.writeText (and/or a fallback) and wrap the call in try/catch to avoid runtime errors.
| navigator.clipboard | |
| .writeText(TABS[activeTab].code.trim()) | |
| .then(() => { | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| }) | |
| .catch((err) => { | |
| console.error('Failed to copy:', err); | |
| }); | |
| const textToCopy = TABS[activeTab].code.trim(); | |
| try { | |
| if (!navigator.clipboard?.writeText) { | |
| throw new Error('Clipboard API is not available'); | |
| } | |
| navigator.clipboard | |
| .writeText(textToCopy) | |
| .then(() => { | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| }) | |
| .catch((err) => { | |
| console.error('Failed to copy:', err); | |
| }); | |
| } catch (err) { | |
| console.error('Failed to copy:', err); | |
| } |
| .then(() => { | ||
| setCopied(true); | ||
| setTimeout(() => setCopied(false), 2000); | ||
| }) |
There was a problem hiding this comment.
The setTimeout(() => setCopied(false), 2000) isn’t cleared on unmount. If the user navigates away quickly, React can warn about setting state on an unmounted component. Store the timeout id (e.g., in a ref) and clear it in an effect cleanup (and also clear/replace any existing timeout when copying again).
| <div className={styles.tabBar}> | ||
| {TABS.map((tab, idx) => ( | ||
| <button | ||
| key={idx} | ||
| className={clsx( | ||
| styles.tab, | ||
| activeTab === idx && styles.tabActive | ||
| )} | ||
| onClick={() => handleTabClick(idx)} | ||
| aria-selected={activeTab === idx} | ||
| role="tab"> |
There was a problem hiding this comment.
The tab UI uses role="tab" on buttons, but the parent container isn’t marked as a tablist, and the tabs aren’t wired to a tabpanel (id/aria-controls) or keyboard navigation (arrow keys/Home/End). This makes the control incomplete for screen reader and keyboard users. Consider using the full WAI-ARIA tabs pattern (tablist + tabs + tabpanel + roving tabindex + key handlers).
There was a problem hiding this comment.
✅ Fixed in 7daeaae — added role="tablist" to tab bar, id/aria-controls on tabs, role="tabpanel" with aria-labelledby on code area
| <button | ||
| key={idx} | ||
| className={clsx( | ||
| styles.tab, | ||
| activeTab === idx && styles.tabActive | ||
| )} | ||
| onClick={() => handleTabClick(idx)} | ||
| aria-selected={activeTab === idx} | ||
| role="tab"> |
There was a problem hiding this comment.
These <button> elements don’t specify type="button". If this component is ever rendered inside a <form>, the default type="submit" can cause unexpected form submissions. Set type="button" for non-submit buttons (tabs and copy button).
There was a problem hiding this comment.
✅ Fixed in 7daeaae — added type="button" to all tab buttons to prevent form submission
| <button | ||
| className={clsx( | ||
| styles.copyButton, | ||
| copied && styles.copyButtonCopied | ||
| )} | ||
| onClick={handleCopy} | ||
| aria-label={copied ? 'Copied!' : 'Copy code'} | ||
| title={copied ? 'Copied!' : 'Copy to clipboard'}> |
There was a problem hiding this comment.
This copy button should also set type="button" to avoid acting as an implicit submit button if rendered within a form context.
There was a problem hiding this comment.
✅ Fixed in 7daeaae — added type="button" to copy button
Document the design patterns, conventions, and principles followed in the website components, including: - Component structure (CSS Modules) - Theme system (Infima variables, alternating backgrounds) - Interactive components (Prism highlighting, icons, state management) - Responsive design and accessibility - SSR safety patterns - Styling best practices - Testing checklist This serves as a reference for maintaining consistency in future website updates. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…mepage - Add CSS variables for accent greens in custom.css: - --zio-accent-green: #81c784 (main accent) - --zio-accent-green-light: #a5d6a7 (lighter variant) - --zio-accent-green-pale: #e8f5e9 (pale variant for gradients) - Replace all hardcoded green colors with variables in: - HomepageHero (button, text, icons, gradients) - HomepageCodeSnippet (tabs, buttons, accents) - Ensure harmony and consistency across light and dark modes This creates a unified design system where all green accents reference the same color palette, making future theme changes easier. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Add 3px accent green top border to feature cards - Change feature card border to accent green on hover - Feature card icons transition to accent green on hover - Creates visual cohesion between code snippet and features sections Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ent green Change featuresSubtitle from var(--ifm-color-primary) to var(--zio-accent-green) to ensure consistent green color across the homepage sections. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…n variable Replace all remaining instances of var(--ifm-color-primary) with var(--zio-accent-green) in: - HomepageFeatures: card headers and icons - HomepageEcosystem: subtitle and card headers - HomepageUsers: user names and titles - HomepageZionomicon: subtitle Now all green text throughout the homepage uses the same unified accent green color (#81c784) for perfect visual consistency. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…mary the accent green - Update primary color palette in custom.css to use #81c784 (accent green) as the main primary color - Remove redundant --zio-accent-green variable and its variants - Replace all var(--zio-accent-green) references with var(--ifm-color-primary) - Update gradient colors to use hardcoded values (#e8f5e9, #a5d6a7) This eliminates color duplication and creates a single, unified primary color throughout the entire homepage. The primary color now represents the accent green used consistently across all sections. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <div> | ||
| <Link | ||
| className="button button--outline button--lg" | ||
| to="/docs"> |
There was a problem hiding this comment.
to="/docs" likely points to a non-existent route in this site: the Docusaurus config sets docs.routeBasePath to '/', so docs are served from the root (and /docs may 404). Update this link to a valid docs URL for this site (e.g., a specific doc like /installation or the doc index route used by the navbar).
| to="/docs"> | |
| to="/"> |
| <div className={styles.tabBar}> | ||
| {TABS.map((tab, idx) => ( | ||
| <button | ||
| key={idx} | ||
| className={clsx( | ||
| styles.tab, | ||
| activeTab === idx && styles.tabActive | ||
| )} | ||
| onClick={() => handleTabClick(idx)} | ||
| aria-selected={activeTab === idx} | ||
| role="tab"> | ||
| {tab.label} |
There was a problem hiding this comment.
The tab UI is missing required ARIA structure for tabs: the container should have role="tablist", each tab should have an id, and the code panel should be a role="tabpanel" with aria-labelledby/aria-controls wiring. Also consider adding keyboard navigation (ArrowLeft/ArrowRight, Home/End) per WAI-ARIA tabs pattern.
There was a problem hiding this comment.
✅ Fixed in 7daeaae — added complete ARIA structure: role="tablist", id on tabs, aria-controls/aria-labelledby wiring, and role="tabpanel"
| const handleCopy = () => { | ||
| if (!isBrowser) return; | ||
| navigator.clipboard | ||
| .writeText(TABS[activeTab].code.trim()) | ||
| .then(() => { | ||
| setCopied(true); | ||
| setTimeout(() => setCopied(false), 2000); | ||
| }) |
There was a problem hiding this comment.
handleCopy schedules a setTimeout without cleanup; if the component unmounts within 2s this can trigger a state update on an unmounted component. Store the timeout id in a ref and clear it in a useEffect cleanup (and before scheduling a new one).
There was a problem hiding this comment.
✅ Fixed in 7daeaae — added useRef to store timeout ID with proper cleanup in useEffect and when copying again
| transition: color 0.3s ease; | ||
| } | ||
|
|
||
| .featureCard:hover .featureCardIcon svg { | ||
| color: var(--ifm-color-primary); |
There was a problem hiding this comment.
The new hover color styling for .featureCardIcon likely has no effect because the imported Undraw SVGs use hard-coded fill attributes (not currentColor). If the intent is to tint icons via CSS, update the SVGs to use fill="currentColor" (or strip hard-coded fills) and/or apply fill: currentColor to the relevant SVG/path selectors.
| transition: color 0.3s ease; | |
| } | |
| .featureCard:hover .featureCardIcon svg { | |
| color: var(--ifm-color-primary); | |
| fill: currentColor; | |
| transition: color 0.3s ease, fill 0.3s ease; | |
| } | |
| .featureCardIcon svg * { | |
| fill: currentColor; | |
| transition: fill 0.3s ease; | |
| } | |
| .featureCard:hover .featureCardIcon svg { | |
| color: var(--ifm-color-primary); | |
| fill: currentColor; |
There was a problem hiding this comment.
✅ Fixed in 7daeaae — added fill: currentColor to SVG elements and fill transition to enable color inheritance on hover
Update custom.css to apply the consolidated accent green color (#81c784) as the primary color across both light and dark themes. This ensures consistent coloring throughout the website. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Add timeout cleanup on unmount using useRef/useEffect to prevent state updates - Add navigator.clipboard optional chaining and try/catch for safety - Add type="button" to tab and copy buttons to prevent form submissions - Add proper ARIA structure: tablist, tabpanel, aria-controls, aria-labelledby - Fix copy button timeout to clear previous timeout when switching tabs - Fix horizontal scroll on code blocks: change overflow-y to overflow - Fix feature card icon hover: add fill: currentColor for SVG fill inheritance - Change docs link from /docs to / (correct route) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Summary
Adds a new interactive
HomepageCodeSnippetcomponent to the website homepage that replaces the existingHomepageShowcasessection. The new component features:prism-react-rendererwith dracula themevar(--ifm-color-emphasis-100)to alternate with adjacent sectionsImplementation Details
website/src/components/HomepageCodeSnippet/index.js: React component with tab state management, Prism integration, copy functionalitystyles.module.css: CSS Modules with responsive grid layout, dark code panel stylingwebsite/src/pages/index.jsto swapHomepageShowcases→HomepageCodeSnippetTest Plan
🤖 Generated with Claude Code