Skip to content

Conversation

@rjt-rockx
Copy link

@rjt-rockx rjt-rockx commented Jan 4, 2026

Description

Redesign of the documentation site styling, focusing on improved readability, performance, and maintainability.

What changed:

  • Design token system using CSS custom properties for colors, shadows, spacing
  • Self-hosted fonts (Instrument Sans, Figtree) - removed Google Fonts dependency
  • Monokai/Monokai Light syntax highlighting with proper token coverage
  • Scroll spy for TOC using IntersectionObserver
  • Improved dark mode with better contrast
  • Component cards with better layout and text handling
  • Code blocks with borders and improved copy feedback
  • Progressive enhancement for newer CSS (@supports wrappers for interpolate-size, ::details-content)
  • Mobile nav/sidebar improvements
  • Animated details/accordion elements
  • Lint support for woff2 font files

Related issue (if applicable): N/A

Pull request in esphome with YAML changes (if applicable): N/A

Checklist:

  • I am merging into next because this is new documentation that has a matching pull-request in esphome as linked above.
    or

  • I am merging into current because this is a fix, change and/or adjustment in the current documentation and is not for a new component or feature.

  • Link added in /components/_index.md when creating new documents for new components or cookbook.

- Refactor main.css with improved layout and responsive styles
- Update SVG icons with consistent styling
- Simplify sidebar and menu JavaScript
- Clean up baseof.html and index layout
- Minor content adjustments to index and getting started guide
- Implement consistent grid layout for component cards
- Add 60/40 split between image and text areas
- Improve text wrapping with text-wrap: pretty
- Add background color differentiation for text area
- Increase card size and minimum dimensions
- Remove link styling from component cards
- Refine color palette with improved contrast
- Add new design tokens (heading-color, text-secondary, surface-color)
- Update navigation height and styling
- Improve border colors and spacing
- Add theme transition disabling utility
- Improve blockquote and alert styling
- Update heading markup rendering
- Refine project and single page layouts
- Update shortcode templates for consistency
- Update existing icon SVGs for consistency
- Add new icons (sun, moon, check, close, etc.)
- Standardize icon formatting
@esphome esphome bot added the current label Jan 4, 2026
@netlify
Copy link

netlify bot commented Jan 4, 2026

Deploy Preview for esphome ready!

Name Link
🔨 Latest commit 50ff6db
🔍 Latest deploy log https://app.netlify.com/projects/esphome/deploys/696c19292256bd00087055c5
😎 Deploy Preview https://deploy-preview-5854--esphome.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 4, 2026

Walkthrough

Normalized copy and icon identifiers across content, replaced emoji alert icons with SVGs, introduced theme-aware CSS variables and typography, reworked sidebar/TOC rendering, enhanced client JS for menu/TOC/theme (scroll‑spy with pause/resume), adjusted templates/shortcodes, and added dev-mode asset cache‑busting.

Changes

Cohort / File(s) Summary
Content & Markdown
content/_index.md, content/components/_index.md, content/guides/getting_started_hassio.md
Text casing/phrasing normalization, icon identifier updates, replaced long help block with a TIP callout, minor formatting tweaks.
Site Menu Config
hugo.yaml
Menu display names re‑cased to sentence style; URLs/weights unchanged.
Theme CSS (large)
themes/esphome-theme/assets/css/main.css, themes/esphome-theme/assets/css/chroma.css
Added fonts, global CSS variables and dark theme overrides, replaced hard‑coded tokens with variables, extensive layout/component/style and responsive updates, regenerated syntax highlight styles.
Client JS — main
themes/esphome-theme/assets/js/main.js
Added details/summary fallback, clipboard copy feedback via class toggle, IntersectionObserver-based scroll‑spy with pause/resume during programmatic TOC scrolling, TOC link smooth-scroll handling and URL/hash updates.
Client JS — menu
themes/esphome-theme/assets/js/menu.js
Coordinated mobile menu/TOC/overlay behavior, overlay click handling, dropdown/keyboard support, mobile theme picker, transient transition disabling when switching theme, and global close/resize handlers.
Search UI
themes/esphome-theme/assets/js/search.js
Placeholder changed from Search... to Search.
Alert & Heading Markup
themes/esphome-theme/layouts/_default/_markup/render-blockquote-alert.html, themes/esphome-theme/layouts/_default/_markup/render-heading.html
Replaced emoji-based alerts with inline SVG includes; changed copy-link markup to a span containing link and check icons.
Base Layout & Navigation
themes/esphome-theme/layouts/_default/baseof.html
Added release/version nav item, swapped hamburger for SVGs, added desktop theme toggle and mobile theme picker, nav search icon, overlay element, and removed build-info popup/markup.
Removed/Adjusted Overlay Nodes
themes/esphome-theme/layouts/_default/list.html, themes/esphome-theme/layouts/_default/projects.html, themes/esphome-theme/layouts/_default/single.html
Removed duplicate overlay <div id="overlay"> occurrences (verify remaining references in JS).
Homepage & CTAs
themes/esphome-theme/layouts/index.html
Hero heading casing adjusted; CTAs moved inline into hero and standalone CTA block removed.
Asset Handling
themes/esphome-theme/layouts/partials/asset.html
Dev-mode cache‑busting: append ?v={{ now.Unix }} to CSS/JS relPermalink in development.
Sidebar / TOC Rendering
themes/esphome-theme/layouts/partials/sidebar.html
Removed alphabetic TOC sort UI/logic; simplified to a single page-toc iteration with optional nested rendering.
Shortcodes & Small Markup
themes/esphome-theme/layouts/shortcodes/changelogs.html, .../collapse.html, .../getting-started-grid.html, .../imgtable.html
Added visible summary labels ("All Changelogs", "Show details"), changed getting-started button to btn-secondary, wrapped component card text in .component-text.
Codeblock Markup
themes/esphome-theme/layouts/_default/_markup/render-codeblock.html
Replaced copy button SVG with new inline SVGs (copy-icon, copy-check) and removed prior copy-feedback span.
Template Icons & Assets
themes/esphome-theme/layouts/partials/asset.html, themes/esphome-theme/layouts/_default/_markup/render-blockquote-alert.html
Use of static SVG files for icons; dev asset URLs cache-busted.
Linting Script
lint.py
Added .woff2 to file_types and binary_types; skip UTF‑8/content checks for binary suffixes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant Browser
  participant MenuLogic as Menu/Overlay Logic
  participant TOC as TOC Link Handler
  participant Headings as Page Headings
  participant Observer as IntersectionObserver

  Note over MenuLogic,Observer: Menu/TOC/scroll‑spy & theme interactions
  User->>Browser: Click hamburger / open menu
  Browser->>MenuLogic: openMenu() -> show overlay, set menu state
  User->>TOC: Click TOC link
  TOC->>Observer: pause scroll‑spy
  TOC->>Headings: smooth scroll to target (programmatic)
  Headings-->>TOC: reached target
  TOC->>Observer: resume scroll‑spy after delay
  Observer->>TOC: highlight active TOC entry based on topmost visible heading
  User->>Browser: Select theme option
  Browser->>MenuLogic: setTheme() -> temporarily disable transitions, persist choice, update data-theme
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jesserockz
  • clydebarrow
  • guillempages
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: documentation site redesign' accurately and concisely summarizes the main objective of the pull request, which is a comprehensive redesign of the documentation site's styling and functionality.
Description check ✅ Passed The description clearly explains the redesign effort, lists specific improvements (design tokens, self-hosted fonts, syntax highlighting, scroll spy, dark mode, component cards, code blocks, mobile nav, details animations, woff2 support), and maintains relevance throughout.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
themes/esphome-theme/layouts/partials/sidebar.html (1)

20-20: Remove safeHTML from heading title output to prevent XSS vulnerabilities.

The .Title values come from Markdown heading content (user-editable and untrusted). The replaceRE filter only removes trailing whitespace and does not sanitize HTML tags or script content. Using safeHTML then bypasses template escaping, allowing embedded HTML or script payloads in headings to execute unescaped in the DOM. This is especially risky given the unsafe: true setting in hugo.yaml's goldmark renderer config, which permits raw HTML in Markdown.

Remove the | safeHTML pipe to use Hugo's default HTML escaping, which safely handles heading titles. If HTML in headings is intentional, sanitize the content server-side before rendering or override the heading render hook to explicitly escape dangerous content.

themes/esphome-theme/assets/css/main.css (2)

1732-1741: Hardcoded color in figcaption should use CSS variable.

Line 1735 uses hardcoded color: #666 which won't adapt to dark mode. This should use --text-secondary for theme consistency.

Suggested fix
 figcaption {
   margin-top: 0.75em;
   font-size: 0.9em;
-  color: #666;
+  color: var(--text-secondary);
   font-style: italic;
   line-height: 1.4;
   max-width: 80%;
   margin-left: auto;
   margin-right: auto;
 }

7-65: Address WCAG AA color contrast compliance issue.

The design token system is well-structured with good semantic naming. However, verification found that --feature-card-text-secondary: rgba(0, 0, 0, 0.55) on --feature-card-bg: #ffffff produces a contrast ratio of only 3.36:1, which fails WCAG AA requirement of 4.5:1 for normal text. Adjust the secondary text color to meet accessibility compliance.

🧹 Nitpick comments (8)
themes/esphome-theme/layouts/_default/_markup/render-blockquote-alert.html (1)

11-11: Consider adding error handling for missing alert types or icon files.

The current implementation will cause a build failure if:

  1. An alert type is used that's not defined in the $icons dictionary
  2. A referenced SVG file doesn't exist

While build-time failures are acceptable for a documentation site, consider adding a fallback icon or validation to provide clearer error messages for maintainers.

🔎 Optional: Add a default fallback icon
-    <span class="alert-icon">{{ os.ReadFile (printf "static/images/icons/%s.svg" (index $icons .AlertType)) | safeHTML }}</span>
+    <span class="alert-icon">{{ os.ReadFile (printf "static/images/icons/%s.svg" (index $icons .AlertType | default "circle-info")) | safeHTML }}</span>

This ensures unknown alert types fall back to the circle-info icon rather than breaking the build.

themes/esphome-theme/layouts/index.html (1)

6-10: Consider adding rel="noopener noreferrer" to the external link.

The Device database link points to an external domain. While devices.esphome.io is a trusted ESPHome property, adding security attributes is a defensive best practice for external links.

🔎 Suggested change
         <div class="cta-buttons">
             <a href="{{ "guides/getting_started_hassio/" | relURL }}" class="btn btn-primary">Get started</a>
             <a href="{{ "components/" | relURL }}" class="btn btn-secondary">Browse components</a>
-            <a href="https://devices.esphome.io/" class="btn btn-secondary">Device database</a>
+            <a href="https://devices.esphome.io/" class="btn btn-secondary" rel="noopener noreferrer">Device database</a>
         </div>
themes/esphome-theme/assets/js/main.js (2)

292-296: Add an initial value to reduce() to prevent potential runtime error.

If visibleEntries is filtered to a non-empty array but somehow becomes empty during the reduce operation due to a race condition or browser quirk, calling reduce() without an initial value on an empty array throws a TypeError.

🔎 Suggested fix
                 if (visibleEntries.length > 0) {
                     // Get the topmost visible heading
-                    const topEntry = visibleEntries.reduce((top, entry) => {
-                        return entry.boundingClientRect.top < top.boundingClientRect.top ? entry : top;
-                    });
+                    const topEntry = visibleEntries.reduce((top, entry) => {
+                        return entry.boundingClientRect.top < top.boundingClientRect.top ? entry : top;
+                    }, visibleEntries[0]);

338-342: Consider extracting the magic number 80 into a named constant.

The offset 80 appears to correspond to the navigation bar height. If this value changes (e.g., via CSS updates), it could cause misalignment. Consider deriving it from the actual nav height or a shared constant.

🔎 Suggested approach
+                        const navOffset = document.getElementById('nav-container')?.offsetHeight || 80;
                         window.scrollTo({
-                            top: targetElement.offsetTop - 80,
+                            top: targetElement.offsetTop - navOffset,
                             behavior: 'smooth'
                         });
themes/esphome-theme/assets/css/main.css (4)

919-922: Consider accessibility of display: contents.

Using display: contents (Line 921) can have accessibility implications in some older browser versions where it removes elements from the accessibility tree. Since this is applied to icon containers within a button that has proper semantics, the impact should be minimal, but worth noting.

Alternative approach using visibility
 [data-theme="dark"] .moon-icon,
 [data-theme="light"] .sun-icon {
-  display: contents;
+  display: block;
 }
+
+[data-theme="dark"] .sun-icon,
+[data-theme="light"] .moon-icon {
+  display: none;
+}

1272-1284: Consider using CSS variables for button colors.

The primary button uses hardcoded color values (#00a8e0, #fff) instead of CSS variables. While functional, using variables like --btn-primary-color (which exists) and a new --btn-primary-text would improve maintainability and theme consistency.

Suggested refactor using variables
 .btn-primary {
-  background-color: #00a8e0;
-  color: #fff;
+  background-color: var(--btn-primary-color);
+  color: var(--btn-text-color-inverse, #fff);
 }

Add to :root:

--btn-text-color-inverse: #fff;

1058-1084: Consider reducing !important usage in dark mode overrides.

Several dark mode rules use !important (e.g., Lines 1059, 1071-1073, 1078). While this ensures overrides work, it can make future maintenance harder. Since CSS variables already handle most theming, consider whether these !important declarations are necessary or if they're compensating for specificity issues elsewhere.


1604-1609: Duplicate .cta-buttons rule block.

This .cta-buttons block duplicates the one at Lines 1239-1244. While CSS allows multiple blocks that get merged, consolidating them would improve maintainability.

Consolidated rule
.cta-buttons {
  display: flex;
  flex-wrap: wrap;
  flex-flow: row wrap;
  justify-content: flex-start;
  gap: var(--grid-gap-sm);
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b5f40f and e2e2c03.

⛔ Files ignored due to path filters (49)
  • static/images/icons/account-tie.svg is excluded by !**/*.svg
  • static/images/icons/bolt.svg is excluded by !**/*.svg
  • static/images/icons/briefcase.svg is excluded by !**/*.svg
  • static/images/icons/check.svg is excluded by !**/*.svg
  • static/images/icons/chip.svg is excluded by !**/*.svg
  • static/images/icons/circle-exclamation.svg is excluded by !**/*.svg
  • static/images/icons/circle-info.svg is excluded by !**/*.svg
  • static/images/icons/close.svg is excluded by !**/*.svg
  • static/images/icons/cloud-download.svg is excluded by !**/*.svg
  • static/images/icons/code-block-tags.svg is excluded by !**/*.svg
  • static/images/icons/code.svg is excluded by !**/*.svg
  • static/images/icons/comments.svg is excluded by !**/*.svg
  • static/images/icons/compass.svg is excluded by !**/*.svg
  • static/images/icons/computer.svg is excluded by !**/*.svg
  • static/images/icons/database.svg is excluded by !**/*.svg
  • static/images/icons/desktop-tower-monitor.svg is excluded by !**/*.svg
  • static/images/icons/discord.svg is excluded by !**/*.svg
  • static/images/icons/edit.svg is excluded by !**/*.svg
  • static/images/icons/espressif.svg is excluded by !**/*.svg
  • static/images/icons/github.svg is excluded by !**/*.svg
  • static/images/icons/head-cog.svg is excluded by !**/*.svg
  • static/images/icons/history.svg is excluded by !**/*.svg
  • static/images/icons/home-automation.svg is excluded by !**/*.svg
  • static/images/icons/home.svg is excluded by !**/*.svg
  • static/images/icons/industry.svg is excluded by !**/*.svg
  • static/images/icons/kitchen-set.svg is excluded by !**/*.svg
  • static/images/icons/lightbulb.svg is excluded by !**/*.svg
  • static/images/icons/link.svg is excluded by !**/*.svg
  • static/images/icons/list.svg is excluded by !**/*.svg
  • static/images/icons/magic.svg is excluded by !**/*.svg
  • static/images/icons/memory.svg is excluded by !**/*.svg
  • static/images/icons/menu.svg is excluded by !**/*.svg
  • static/images/icons/microchip.svg is excluded by !**/*.svg
  • static/images/icons/monitor.svg is excluded by !**/*.svg
  • static/images/icons/moon.svg is excluded by !**/*.svg
  • static/images/icons/ohf-logo-on-dark.svg is excluded by !**/*.svg
  • static/images/icons/ohf-logo-on-light.svg is excluded by !**/*.svg
  • static/images/icons/print.svg is excluded by !**/*.svg
  • static/images/icons/puzzle-piece.svg is excluded by !**/*.svg
  • static/images/icons/raspberry-pi.svg is excluded by !**/*.svg
  • static/images/icons/search.svg is excluded by !**/*.svg
  • static/images/icons/shape-plus.svg is excluded by !**/*.svg
  • static/images/icons/shield-alt.svg is excluded by !**/*.svg
  • static/images/icons/sun.svg is excluded by !**/*.svg
  • static/images/icons/terminal.svg is excluded by !**/*.svg
  • static/images/icons/triangle-exclamation.svg is excluded by !**/*.svg
  • static/images/icons/user-cog.svg is excluded by !**/*.svg
  • static/images/icons/wifi-strength-off.svg is excluded by !**/*.svg
  • static/images/icons/wifi.svg is excluded by !**/*.svg
📒 Files selected for processing (21)
  • content/_index.md
  • content/components/_index.md
  • content/guides/getting_started_hassio.md
  • hugo.yaml
  • themes/esphome-theme/assets/css/main.css
  • themes/esphome-theme/assets/js/main.js
  • themes/esphome-theme/assets/js/menu.js
  • themes/esphome-theme/assets/js/search.js
  • themes/esphome-theme/layouts/_default/_markup/render-blockquote-alert.html
  • themes/esphome-theme/layouts/_default/_markup/render-heading.html
  • themes/esphome-theme/layouts/_default/baseof.html
  • themes/esphome-theme/layouts/_default/list.html
  • themes/esphome-theme/layouts/_default/projects.html
  • themes/esphome-theme/layouts/_default/single.html
  • themes/esphome-theme/layouts/index.html
  • themes/esphome-theme/layouts/partials/asset.html
  • themes/esphome-theme/layouts/partials/sidebar.html
  • themes/esphome-theme/layouts/shortcodes/changelogs.html
  • themes/esphome-theme/layouts/shortcodes/collapse.html
  • themes/esphome-theme/layouts/shortcodes/getting-started-grid.html
  • themes/esphome-theme/layouts/shortcodes/imgtable.html
💤 Files with no reviewable changes (4)
  • content/guides/getting_started_hassio.md
  • themes/esphome-theme/layouts/_default/list.html
  • themes/esphome-theme/layouts/_default/single.html
  • themes/esphome-theme/layouts/_default/projects.html
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

  • Do not generate or add any sequence diagrams

Files:

  • content/components/_index.md
  • themes/esphome-theme/assets/js/search.js
  • themes/esphome-theme/layouts/shortcodes/collapse.html
  • themes/esphome-theme/layouts/partials/asset.html
  • themes/esphome-theme/layouts/_default/_markup/render-blockquote-alert.html
  • themes/esphome-theme/layouts/partials/sidebar.html
  • themes/esphome-theme/layouts/shortcodes/changelogs.html
  • themes/esphome-theme/layouts/shortcodes/imgtable.html
  • content/_index.md
  • themes/esphome-theme/layouts/index.html
  • themes/esphome-theme/layouts/shortcodes/getting-started-grid.html
  • themes/esphome-theme/layouts/_default/baseof.html
  • themes/esphome-theme/assets/css/main.css
  • hugo.yaml
  • themes/esphome-theme/layouts/_default/_markup/render-heading.html
  • themes/esphome-theme/assets/js/menu.js
  • themes/esphome-theme/assets/js/main.js
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: esphome/esphome-docs PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-24T00:13:13.459Z
Learning: Open pull requests against the correct base branch: use `current` for documentation-only revisions and `next` for changes tied to new features in esphome/esphome
Learnt from: jesserockz
Repo: esphome/esphome-docs PR: 4865
File: .github/workflows/needs-docs.yml:0-0
Timestamp: 2025-05-01T03:29:47.922Z
Learning: In the esphome-docs repository, the "current" label is automatically added by a bot to pull requests, making it a reliable indicator for the target branch.
📚 Learning: 2025-05-05T05:08:25.575Z
Learnt from: clydebarrow
Repo: esphome/esphome-docs PR: 0
File: :0-0
Timestamp: 2025-05-05T05:08:25.575Z
Learning: In ESPHome documentation, leading slashes in doc references are necessary when the source document is not at the root of the tree, as absolute paths to referenced documents are required in this context.

Applied to files:

  • content/components/_index.md
📚 Learning: 2025-05-01T03:29:47.922Z
Learnt from: jesserockz
Repo: esphome/esphome-docs PR: 4865
File: .github/workflows/needs-docs.yml:0-0
Timestamp: 2025-05-01T03:29:47.922Z
Learning: In the esphome-docs repository, the "current" label is automatically added by a bot to pull requests, making it a reliable indicator for the target branch.

Applied to files:

  • content/components/_index.md
📚 Learning: 2025-05-12T00:02:50.869Z
Learnt from: jesserockz
Repo: esphome/esphome-docs PR: 4901
File: changelog/2025.4.0.rst:124-126
Timestamp: 2025-05-12T00:02:50.869Z
Learning: In the ESPHome changelog structure, the "All changes" section should have the `:open:` attribute to be expanded by default, but the "Dependency Changes" section should NOT have this attribute as it should remain collapsed by default.

Applied to files:

  • themes/esphome-theme/layouts/shortcodes/collapse.html
  • themes/esphome-theme/layouts/partials/sidebar.html
  • themes/esphome-theme/layouts/shortcodes/changelogs.html
  • content/_index.md
  • themes/esphome-theme/layouts/index.html
  • themes/esphome-theme/layouts/_default/baseof.html
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (32)
themes/esphome-theme/layouts/partials/asset.html (1)

5-12: LGTM! Appropriate cache-busting for development.

Adding timestamp-based cache-busting to development assets ensures browsers always fetch the latest CSS and JS files during local development. Production assets correctly remain unchanged with fingerprinting and integrity checks.

themes/esphome-theme/layouts/_default/_markup/render-blockquote-alert.html (1)

1-7: All referenced SVG icon files exist.

The icon dictionary correctly maps to the following files in static/images/icons/:

  • circle-exclamation.svg
  • circle-info.svg
  • lightbulb.svg
  • triangle-exclamation.svg

No issues identified.

themes/esphome-theme/layouts/partials/sidebar.html (4)

1-8: LGTM!

The variable setup and container initialization are standard Hugo template patterns. The conditional scroll-trap class application is appropriate.


29-40: LGTM!

The section list rendering logic is straightforward and handles edge cases appropriately (missing titles, empty pages).


43-53: LGTM!

The GitHub edit link implementation follows standard Hugo patterns. The inline SVG rendering using os.ReadFile is appropriate for static assets.


14-17: No bug: Empty TOC from missing Level 0 nested headings is handled safely by Hugo.

The edge case where a Level 0 heading lacks nested .Headings is handled gracefully. If (index $headings 0).Headings is nil, $headings becomes nil and the range loop over it produces an empty TOC—not an error. This is correct behavior: Level 0 headings function as containers for actual content headings, so missing children should render nothing. Hugo's with and range constructs safely handle nil and empty values throughout the template.

themes/esphome-theme/assets/js/search.js (1)

102-102: LGTM! Clean placeholder text.

The shortened placeholder aligns with the broader UI text normalization in this PR.

themes/esphome-theme/layouts/shortcodes/changelogs.html (1)

41-41: LGTM! Improved accessibility.

Adding visible summary text improves usability by clearly labeling the collapsible section.

themes/esphome-theme/layouts/shortcodes/getting-started-grid.html (1)

23-23: LGTM! Intentional button styling update.

The class change from btn-primary to btn-secondary aligns with the broader button system refinements in this PR.

themes/esphome-theme/layouts/shortcodes/collapse.html (1)

23-23: LGTM! Improved accessibility.

Providing a visible summary label makes the collapsible section clearer to users and improves keyboard navigation.

hugo.yaml (1)

30-86: LGTM! Consistent menu label normalization.

The capitalization changes from Title Case to sentence case are applied consistently across all main menu items, aligning with the broader text normalization objectives of this PR.

themes/esphome-theme/layouts/_default/_markup/render-heading.html (1)

3-6: LGTM!

The dual-icon approach with a container span is well-structured for the copy-link feedback feature. Good accessibility practices with aria-hidden="true" and empty alt attributes for decorative images.

content/components/_index.md (1)

15-21: LGTM!

The condensed TIP callout is a clear improvement over the verbose multi-paragraph block. It preserves the essential information (GitHub PR, Edit on GitHub, Discord) in a more scannable format that aligns with the PR's goal of streamlined documentation.

themes/esphome-theme/assets/js/main.js (2)

116-121: LGTM!

Defensive fix for empty <summary> elements ensures consistent UX across browsers. The "Show details" default text aligns with typical disclosure widget conventions.


139-141: LGTM!

Simplified copy feedback using a CSS class is cleaner than manual animation triggers. The 1-second timeout provides appropriate visual feedback duration.

content/_index.md (3)

27-45: LGTM!

Icon identifier updates and sentence-case normalization are consistent throughout. The changes align with the PR's broader styling standardization effort.


50-75: LGTM!

Section heading and feature descriptions consistently follow sentence-case conventions, improving visual consistency across the site.


105-134: LGTM!

Getting started section headings and button text normalized to sentence case. Changes are consistent with the rest of the content updates.

themes/esphome-theme/layouts/_default/baseof.html (3)

61-64: Verify font loading performance impact.

External Google Fonts adds a network dependency. The preconnect hints help, but consider self-hosting fonts for improved performance and privacy if this becomes a concern.


108-117: LGTM!

The theme toggle implementation is well-structured with:

  • Desktop toggle button with sun/moon icons
  • Mobile-specific picker with explicit Light/Dark options
  • Proper aria-label for accessibility

Good separation of desktop and mobile UX patterns.


155-159: LGTM!

Using separate light/dark logo images with CSS class-based visibility (light-only/dark-only) is a clean approach for theme-aware branding without JavaScript.

themes/esphome-theme/assets/js/menu.js (4)

11-19: LGTM!

Proper state coordination - overlay is only hidden if the TOC isn't currently open, preventing UI glitches when closing one panel while another is active.


59-71: LGTM!

The double requestAnimationFrame is a well-known pattern to ensure CSS transitions are properly disabled during theme changes and re-enabled only after the browser has painted the new theme. This prevents flash-of-transition artifacts.


76-94: LGTM!

Theme toggle handlers correctly use stopPropagation() to prevent menu closure when clicking theme controls. The initial active state setup ensures the mobile picker reflects the current theme on page load.


105-110: LGTM!

Overlay click handler properly closes both TOC and menu, ensuring consistent dismissal behavior regardless of which panel triggered the overlay.

themes/esphome-theme/assets/css/main.css (4)

2-5: LGTM - Theme transition disable utility.

Good approach to prevent flash-of-unstyled-content during theme switches by disabling all transitions with !important. The universal selector with descendant combinator ensures comprehensive coverage.


773-781: Good focus state implementation.

The search input properly replaces the default outline with a visible custom focus indicator (border-color change and box-shadow). This maintains keyboard accessibility while matching the design system.


1847-1870: Clever clip-path animation for mobile menu.

The use of clip-path: inset() for the mobile menu reveal animation is smooth and performant. The negative bottom inset (-20px) on the active state correctly accounts for the box-shadow overflow. The pointer-events toggle ensures proper interaction states.


1143-1152: Good use of pseudo-element for full-bleed sidebar background.

The ::before pseudo-element extending 50vw to the left is a clean technique for creating a full-bleed background effect without affecting layout. The pointer-events: none correctly prevents interaction interference.

themes/esphome-theme/layouts/shortcodes/imgtable.html (3)

51-59: LGTM - Component text wrapper addition.

The new <div class="component-text"> wrapper (Lines 51-59) properly groups the component name and caption, aligning with the CSS styling defined in main.css (.component-text at Lines 1643-1650). This improves the component card layout structure.


52-54: Good word-breaking technique for long component names.

The regex-based insertion of zero-width space (&#x200B;) after non-alphabetic characters enables natural word-breaking for long technical names (e.g., ESP32-S3-DevKitC-1 can break at hyphens). The safeHTML call is correctly used since HTML entities are being inserted.


35-43: Caption concatenation logic is clear.

The loop correctly handles the optional parameters: dark-invert is captured as a class, while other non-empty values are treated as captions and concatenated with " & ". This matches the documented usage patterns in the header comment.

@stuartparmenter
Copy link
Contributor

stuartparmenter commented Jan 4, 2026

No meaningful opinion on the case change for 2nd word in titles & buttons, but if we are going to make that change, it should be added to the linter

- Add blank lines after anchor shortcodes in getting_started_hassio.md
- Fix italic formatting (use _ instead of *)
- Remove Google Fonts links, fonts now self-hosted
- Add @font-face declarations for Instrument Sans and Figtree
- Reorganize CSS variables with better grouping and comments
- Add new design tokens (shadows, hover overlays)
- Improve code organization and maintainability
- Update syntax highlighting styles
- Improve codeblock copy button implementation
- Add Instrument Sans and Figtree font files
- Add content-copy icon SVG
woff2 font files were being incorrectly parsed as text, causing
lint failures. Added binary_types list and skip them alongside
image_types in content check loop.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
themes/esphome-theme/assets/js/main.js (1)

157-164: Remove dead code and consider consistent feedback duration.

The copy-feedback element no longer exists in render-codeblock.html (lines 3-8 show only SVG icons now), making lines 157-160 dead code. Additionally, the timeout here is 2000ms vs 1000ms for .copy-link (line 141).

Proposed cleanup
 try {
     await navigator.clipboard.writeText(code);
-    const feedback = button.querySelector(".copy-feedback");
-    if (feedback) {
-        feedback.textContent = "Copied!";
-    }
     button.classList.add('copied');
     setTimeout(() => {
         button.classList.remove('copied');
-    }, 2000);
+    }, 1000);
 } catch (err) {
     console.error('Failed to copy:', err);
 }
🧹 Nitpick comments (2)
themes/esphome-theme/assets/js/main.js (1)

135-143: Clean implementation; consider adding error handling.

The simplified approach using the copied class is cleaner than animation-based feedback. The 1-second feedback duration is appropriate.

Optional: Add error handling for clipboard operations
 document.querySelectorAll('.copy-link').forEach(button => {
     button.addEventListener('click', async () => {
         const anchor = button.getAttribute('data-anchor');
         const url = `${window.location.origin}${window.location.pathname}#${anchor}`;
-        await navigator.clipboard.writeText(url);
-        button.classList.add('copied');
-        setTimeout(() => button.classList.remove('copied'), 1000);
+        try {
+            await navigator.clipboard.writeText(url);
+            button.classList.add('copied');
+            setTimeout(() => button.classList.remove('copied'), 1000);
+        } catch (err) {
+            console.error('Failed to copy link:', err);
+        }
     });
 });
themes/esphome-theme/assets/css/main.css (1)

1776-1784: Consider refactoring to avoid !important declarations.

The !important flags on lines 1777 and 1782 suggest specificity conflicts. While functional, this approach can make future maintenance harder.

Optional: Investigate specificity and remove !important if possible

Consider increasing selector specificity instead:

.component-card a h2,
.component-card a h3,
.component-card a p,
.component-card a {
  color: var(--component-card-text);
  text-decoration: none;
}

.component-card a:hover {
  color: var(--component-card-text);
  text-decoration: none;
}

Or restructure to avoid conflicts altogether by ensuring component-card styles have adequate specificity naturally.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2e2c03 and b3457d4.

⛔ Files ignored due to path filters (5)
  • static/fonts/Figtree-Italic.woff2 is excluded by !**/*.woff2
  • static/fonts/Figtree.woff2 is excluded by !**/*.woff2
  • static/fonts/InstrumentSans-Italic.woff2 is excluded by !**/*.woff2
  • static/fonts/InstrumentSans.woff2 is excluded by !**/*.woff2
  • static/images/icons/content-copy.svg is excluded by !**/*.svg
📒 Files selected for processing (6)
  • content/guides/getting_started_hassio.md
  • themes/esphome-theme/assets/css/chroma.css
  • themes/esphome-theme/assets/css/main.css
  • themes/esphome-theme/assets/js/main.js
  • themes/esphome-theme/layouts/_default/_markup/render-codeblock.html
  • themes/esphome-theme/layouts/_default/baseof.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • content/guides/getting_started_hassio.md
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

  • Do not generate or add any sequence diagrams

Files:

  • themes/esphome-theme/layouts/_default/_markup/render-codeblock.html
  • themes/esphome-theme/layouts/_default/baseof.html
  • themes/esphome-theme/assets/js/main.js
  • themes/esphome-theme/assets/css/main.css
  • themes/esphome-theme/assets/css/chroma.css
🧠 Learnings (1)
📚 Learning: 2025-05-12T00:02:50.869Z
Learnt from: jesserockz
Repo: esphome/esphome-docs PR: 4901
File: changelog/2025.4.0.rst:124-126
Timestamp: 2025-05-12T00:02:50.869Z
Learning: In the ESPHome changelog structure, the "All changes" section should have the `:open:` attribute to be expanded by default, but the "Dependency Changes" section should NOT have this attribute as it should remain collapsed by default.

Applied to files:

  • themes/esphome-theme/assets/js/main.js
🧬 Code graph analysis (1)
themes/esphome-theme/assets/js/main.js (1)
themes/esphome-theme/assets/js/search.js (2)
  • url (72-72)
  • navContainer (189-189)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (20)
themes/esphome-theme/layouts/_default/_markup/render-codeblock.html (1)

2-9: LGTM! Clean icon toggle implementation.

The dual-SVG approach with fill="currentColor" and distinct classes enables smooth state transitions via CSS. The aria-label provides proper accessibility.

themes/esphome-theme/assets/css/chroma.css (2)

1-76: LGTM! Well-structured light theme syntax highlighting.

The Hugo-generated styles use --surface-color for theme-aware backgrounds and provide comprehensive token mappings. Line number styling with user-select: none prevents unwanted selection during copy operations.


77-154: LGTM! Comprehensive dark theme syntax highlighting.

The monokai-based dark theme uses --sidebar-background for the code block background and provides complete token coverage. The additional generic tokens (GenericDeleted, GenericInserted) enhance diff/patch rendering.

themes/esphome-theme/assets/js/main.js (2)

116-121: LGTM! Defensive fix for empty summary elements.

The implementation correctly identifies and fixes empty summary elements by setting a default text. The trim() check properly handles whitespace-only content.


280-354: Excellent scroll-spy implementation with proper state management.

The IntersectionObserver-based scroll-spy correctly identifies the topmost visible heading and updates the active TOC link. The pause/resume mechanism during programmatic scrolling prevents interference and race conditions.

The 500ms resume delay (line 349) appears reasonable for typical smooth-scroll durations, though it's somewhat arbitrary. Consider tuning if users experience TOC state flickering after navigation.

themes/esphome-theme/layouts/_default/baseof.html (7)

97-97: LGTM! Version badge enhances discoverability.

The nav-version link provides quick access to the changelog and displays the current release version. The implementation correctly uses site data for the version text.


100-101: LGTM! Explicit icon markup improves clarity.

The inline SVGs for menu/close icons are loaded via os.ReadFile, eliminating additional HTTP requests. The CSS-based visibility toggle (controlled by aria-expanded) maintains accessibility.


104-113: LGTM! Appropriate theme controls for different contexts.

The desktop toggle button and mobile picker provide suitable UX patterns for their respective contexts. The explicit theme-option buttons in the mobile picker improve discoverability on smaller screens.


130-132: LGTM! Icon-based search UI is more polished.

The inline search icon provides a cleaner, more modern search interface. The container structure supports the pagefind integration and allows for proper icon/input alignment via CSS.


138-138: Overlay element added for mobile interactions.

The overlay provides a backdrop for mobile menu/TOC interactions. Note that AI summaries indicate this element is removed in some other layout files, which may be intentional for different page types.


140-140: LGTM! Conditional homepage class enables targeted styling.

The conditional class application allows for homepage-specific layout adjustments while maintaining a common base template structure.


151-155: LGTM! Theme-aware footer branding ensures optimal contrast.

Providing separate light/dark logo variants ensures proper contrast and readability across both themes. The CSS-based visibility toggle (.light-only / .dark-only) maintains a clean implementation.

themes/esphome-theme/assets/css/main.css (8)

1-32: LGTM! Well-configured self-hosted fonts.

The self-hosted WOFF2 fonts with variable weight ranges and font-display: swap provide optimal performance and eliminate external dependencies. The absolute paths ensure correct loading from any page depth.


35-38: LGTM! Prevents animation jank during theme switches.

The .theme-transition-disabled class with !important ensures instant color changes during theme switching, preventing distracting transition animations.


40-121: LGTM! Comprehensive design token system for light theme.

The well-organized CSS variables provide semantic naming, clear hierarchy, and backward compatibility aliases. The design tokens cover all major UI elements and will ease future theme customization.


123-169: LGTM! Dark theme variables provide appropriate contrast.

The dark theme overrides maintain proper contrast ratios with lighter text on dark backgrounds. The increased shadow opacity and adjusted hover states are appropriate for the dark aesthetic.


639-657: LGTM! Version badge styling is clean and unobtrusive.

The badge styling uses appropriate design tokens and provides subtle hover feedback. The small font size and badge appearance keep it from dominating the navigation.


2163-2240: Modern CSS features with appropriate fallbacks.

The progressive enhancement approach using @supports ensures graceful degradation for browsers without interpolate-size or ::details-content. The custom chevron indicator and smooth transitions enhance UX without breaking compatibility.


2242-2289: LGTM! Copy button styling aligns with updated markup.

The CSS correctly toggles between copy and check icons using the .copied class, matching the dual-SVG implementation in render-codeblock.html. Hover and active states provide clear visual feedback.


1866-2015: LGTM! Mobile theme picker provides clear visual feedback.

The mobile-specific theme picker with explicit Light/Dark options is well-styled with clear active states. The separation between desktop toggle and mobile picker ensures appropriate UX for each context.

- Remove dead code for .copy-feedback element (no longer in markup)
- Normalize timeout to 1000ms for both copy handlers
- Add try/catch error handling to copy-link handler
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ad6e02 and c1b0852.

📒 Files selected for processing (1)
  • themes/esphome-theme/assets/js/main.js
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

  • Do not generate or add any sequence diagrams

Files:

  • themes/esphome-theme/assets/js/main.js
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: esphome/esphome-docs PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-24T00:13:13.459Z
Learning: Open pull requests against the correct base branch: use `current` for documentation-only revisions and `next` for changes tied to new features in esphome/esphome
📚 Learning: 2025-05-12T00:02:50.869Z
Learnt from: jesserockz
Repo: esphome/esphome-docs PR: 4901
File: changelog/2025.4.0.rst:124-126
Timestamp: 2025-05-12T00:02:50.869Z
Learning: In the ESPHome changelog structure, the "All changes" section should have the `:open:` attribute to be expanded by default, but the "Dependency Changes" section should NOT have this attribute as it should remain collapsed by default.

Applied to files:

  • themes/esphome-theme/assets/js/main.js
🧬 Code graph analysis (1)
themes/esphome-theme/assets/js/main.js (1)
themes/esphome-theme/assets/js/search.js (2)
  • url (72-72)
  • navContainer (189-189)
🔇 Additional comments (5)
themes/esphome-theme/assets/js/main.js (5)

116-121: LGTM!

Good defensive fix for empty <summary> elements, ensuring accessibility and usability when summaries lack text content.


139-145: LGTM!

Good addition of error handling for the clipboard API. The try/catch properly guards against permission denials or other clipboard failures.


151-169: LGTM!

Copy button functionality is correctly implemented with consistent 1000ms feedback timing across both copy handlers.


271-278: LGTM!

The expanded selector correctly includes both .toc-entry and .toc-secondary classes, consistent with the scroll-spy query on line 299.


280-316: LGTM!

The IntersectionObserver-based scroll-spy is well-implemented:

  • Proper guard against empty arrays before reduce
  • Efficient single currentActive tracking avoids DOM thrashing
  • rootMargin correctly accounts for the fixed header offset

@rjt-rockx rjt-rockx changed the title style: improve documentation site styling and component cards feat: documentation site redesign Jan 4, 2026
@rjt-rockx
Copy link
Author

No meaningful opinion on the case change for 2nd word in titles & buttons, but if we are going to make that change, it should be added to the linter

Looked into this a bit and I'm not sure it'd be feasible to enforce that as a lint rule since we're factoring in names and grammar and such. It's more a stylistic decision to prefer sentence case over title case really, and while it might make sense to document that in a style guide or design system, I feel we'd just end up dealing with a ton of false positives and such @stuartparmenter

- Add spacing tokens (--space-xs through --space-xl)
- Add icon size tokens (--icon-sm, --icon-md)
- Add border radius tokens (--radius-sm, --radius-md, --radius-lg)
- Convert layout values from px to rem (nav-height, max-page-width, sidebar)
- Replace hardcoded border-radius with token variables
- Update component cards to horizontal layout with line-clamped text
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
themes/esphome-theme/assets/css/main.css (1)

198-218: Consider removing duplicate overscroll-behavior declaration.

overscroll-behavior: none is declared on both html (line 201) and body (line 215). Setting it on html alone is typically sufficient. Consider removing the duplicate for cleaner code.

🔎 Suggested change
 body {
   min-height: 100vh;
   margin: 0;
   font-family: var(--font-body);
   line-height: 1.6;
   color: var(--text-color);
   background-color: var(--background-color);
-  overscroll-behavior: none;
   display: flex;
   flex-direction: column;
 }
themes/esphome-theme/assets/js/main.js (1)

319-355: Scroll spy implementation is sound; consider using getBoundingClientRect() for more robust positioning.

The closeTOC() function is defined in menu.js (line 41) and is globally accessible, so the call at line 323 works correctly.

The offset calculation at line 340 using targetElement.offsetTop functions properly but could be more robust. Consider using getBoundingClientRect() instead, which accounts for layout changes more reliably:

top: targetElement.getBoundingClientRect().top + window.scrollY - HEADER_HEIGHT

This approach is less susceptible to issues if the layout ever changes such that offsetParent is not the document body.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1b0852 and 26a42ed.

📒 Files selected for processing (2)
  • themes/esphome-theme/assets/css/main.css
  • themes/esphome-theme/assets/js/main.js
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

  • Do not generate or add any sequence diagrams

Files:

  • themes/esphome-theme/assets/js/main.js
  • themes/esphome-theme/assets/css/main.css
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: esphome/esphome-docs PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-24T00:13:13.459Z
Learning: Open pull requests against the correct base branch: use `current` for documentation-only revisions and `next` for changes tied to new features in esphome/esphome
📚 Learning: 2025-05-12T00:02:50.869Z
Learnt from: jesserockz
Repo: esphome/esphome-docs PR: 4901
File: changelog/2025.4.0.rst:124-126
Timestamp: 2025-05-12T00:02:50.869Z
Learning: In the ESPHome changelog structure, the "All changes" section should have the `:open:` attribute to be expanded by default, but the "Dependency Changes" section should NOT have this attribute as it should remain collapsed by default.

Applied to files:

  • themes/esphome-theme/assets/js/main.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (15)
themes/esphome-theme/assets/js/main.js (4)

116-121: LGTM!

Good accessibility fix to ensure empty <summary> elements have meaningful text for screen readers and users.


139-145: LGTM!

Good error handling for the clipboard API with proper visual feedback timing.


162-164: LGTM!

Consistent 1-second feedback timing across copy interactions.


272-273: LGTM!

Good extraction of HEADER_HEIGHT constant for maintainability, and expanded TOC selector to support the new TOC structure.

themes/esphome-theme/assets/css/main.css (11)

1-33: LGTM!

Well-structured font-face declarations with font-display: swap for good loading performance and variable font weights to reduce HTTP requests.


35-38: LGTM!

The !important declaration is justified here to reliably disable transitions during theme switches, preventing visual artifacts.


40-137: LGTM!

Excellent design token system with clear categorization, consistent naming, and backward-compatibility aliases. Using rem units for spacing improves accessibility.


139-189: LGTM!

Dark theme variables are well-structured with appropriate contrast adjustments and increased shadow opacity for visibility on dark backgrounds.


525-594: LGTM!

Clean TOC styling with smooth transitions and clear visual hierarchy. The border-left indicator for active state provides good visual feedback.


863-867: Modern :has() selector usage looks good.

The :has() selector for focus expansion has broad browser support (Chrome 105+, Safari 15.4+, Firefox 121+). If support for older browsers is needed, the search box remains functional without the expansion effect, providing graceful degradation.


1771-1796: LGTM!

Correct implementation of multi-line text truncation with -webkit-line-clamp. The required -webkit-box display and -webkit-box-orient: vertical properties are properly paired.


2190-2267: Good progressive enhancement for <details> animations.

The @supports blocks for interpolate-size and ::details-content provide graceful degradation — unsupported browsers get functional disclosure widgets without animations. The prefers-reduced-motion query inside the ::details-content block is a nice accessibility touch.


1334-1383: LGTM!

Well-structured button system with clear primary/secondary variants and proper handling of both <button> and <a> elements.


1942-1965: LGTM!

The clip-path animation for the mobile menu is a performant choice that enables smooth reveal animations. The pointer-events toggle properly prevents interaction with hidden menu items.


2269-2315: LGTM!

Clean copy button styling with proper icon swap on the copied state, matching the JavaScript implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants