Skip to content

Fix theming, accessibility, and code quality across 18 components#5

Open
el-mehdi wants to merge 2 commits intomainfrom
fix/theming-accessibility-quality
Open

Fix theming, accessibility, and code quality across 18 components#5
el-mehdi wants to merge 2 commits intomainfrom
fix/theming-accessibility-quality

Conversation

@el-mehdi
Copy link
Copy Markdown
Contributor

Summary

  • Replace hardcoded color classes with CSS variable references across 18 components, enabling proper theming for consumers (no more hardcoded red/gray/white Tailwind classes)
  • Fix accessibility issues: add aria-describedby and aria-invalid to Input/Textarea for screen reader support
  • Fix code quality bugs: Rules of Hooks violation in Dropdown, dead code in Select, deprecated substr() in 5 files, as any casts in Dialog/Drawer/Collapsible

Changes by Category

Theming (12 components)

Component What changed
ThemeToggle 30+ red-*/slate-* classes → --fever-primary CSS variables; 3 inline rgba(239,68,68) box-shadows → color-mix()
Search bg-white, focus:bg-whitebg-[var(--fever-background)]; hover:bg-gray-100 → CSS var
Separator bg-gray-200bg-[var(--fever-border)]
Collapsible border-gray-200, bg-gray-50, hover:bg-gray-100 → CSS variables
Card text-gray-600text-[var(--fever-text-secondary)]
Tabs bg-gray-100, border-gray-200, inactive text/hover grays → CSS variables
Navigation Inactive state text-gray-400/hover:text-white/hover:bg-zinc-800 → CSS variables
PasswordStrength Hardcoded hex colors → CSS variables with fallbacks

Accessibility (2 components)

Component What changed
Input aria-describedby linking helper text; aria-invalid for error state
Textarea aria-describedby linking helper text; aria-invalid for error state

Type Safety (3 components)

Component What changed
Dialog as anyReact.ReactElement<{ onClick?: React.MouseEventHandler }>
Drawer as any → properly typed React.ReactElement
Collapsible as any → properly typed React.ReactElement

Code Quality (5 components)

Component What changed
Select Removed dead duplicate if (!open) return null
Dropdown Fixed Rules of Hooks violation (useDropdownContext() called inline in JSX)
CodeBlock Removed console.error from clipboard error handler
FileUpload, Checkbox, Input, PasswordInput, Textarea substr()slice() (deprecated API)

Test Plan

  • Full build passes (pnpm build)
  • Visual check: ThemeToggle renders correctly in light/dark modes
  • Visual check: Search focus ring uses primary color, not red
  • Visual check: Tabs/Navigation active states use primary color
  • Screen reader test: Input/Textarea error messages announced

Theming:
- Replace hardcoded gray/white/zinc Tailwind classes with CSS variable
  references in Search, Separator, Collapsible, Card, Tabs, Navigation
- Replace 30+ hardcoded red-*/slate-* classes in ThemeToggle with
  --fever-primary/--fever-primary-dark/--fever-primary-light variables
- Replace hardcoded hex colors in PasswordStrength with CSS variables
- Replace hardcoded rgba(239,68,68) inline box-shadows with color-mix()
  using --fever-primary for proper theming

Accessibility:
- Add aria-describedby linking helper/error text to Input and Textarea
- Add aria-invalid attribute for error state in Input and Textarea

Type safety:
- Replace 'as any' casts with properly typed React.ReactElement in
  Dialog (DialogTrigger, DialogClose), Drawer (DrawerClose), and
  Collapsible (CollapsibleTrigger)

Code quality:
- Remove dead duplicate 'if (!open) return null' in Select
- Fix Rules of Hooks violation in Dropdown (useDropdownContext called
  inline in JSX instead of at component top level)
- Replace deprecated substr() with slice() in FileUpload, Checkbox,
  Input, PasswordInput, Textarea
- Remove console.error from CodeBlock clipboard error handler
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @el-mehdi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on improving the theming, accessibility, and code quality of several components. It replaces hardcoded color classes with CSS variables for better theming, fixes accessibility issues in the Input and Textarea components, and resolves various code quality concerns across multiple files.

Highlights

  • Theming: Replaced hardcoded color classes with CSS variable references across 12 components, enabling proper theming.
  • Accessibility: Fixed accessibility issues by adding aria-describedby and aria-invalid to Input/Textarea for screen reader support.
  • Code Quality: Addressed code quality issues, including Rules of Hooks violations, dead code, deprecated substr() usage, and as any casts.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • Card
    • Replaced hardcoded text color with a CSS variable for theming.
  • Checkbox
    • Replaced substr() with slice() for generating checkbox ID.
  • CodeBlock
    • Removed console.error from clipboard error handler.
  • Collapsible
    • Replaced as any cast with properly typed React.ReactElement.
  • Dialog
    • Replaced as any cast with properly typed React.ReactElement.
  • Drawer
    • Replaced as any cast with properly typed React.ReactElement.
  • Dropdown
    • Fixed Rules of Hooks violation by moving useDropdownContext() outside of JSX.
  • FileUpload
    • Replaced substr() with slice() for generating unique ID.
  • Input
    • Added aria-describedby to link helper text for accessibility.
    • Added aria-invalid for error state to improve accessibility.
  • Navigation
    • Replaced hardcoded text and background colors with CSS variables for theming.
  • PasswordInput
    • Replaced substr() with slice() for generating password input ID.
  • PasswordStrength
    • Replaced hardcoded hex colors with CSS variables with fallbacks.
  • Search
    • Replaced hardcoded background colors with CSS variables for theming.
  • Select
    • Removed dead duplicate if (!open) return null statement.
  • Separator
    • Replaced hardcoded border color with a CSS variable for theming.
  • Tabs
    • Replaced hardcoded background and border colors with CSS variables for theming.
  • Textarea
    • Added aria-describedby to link helper text for accessibility.
    • Added aria-invalid for error state to improve accessibility.
  • ThemeToggle
    • Replaced hardcoded color classes with CSS variable references for theming.
    • Replaced inline rgba box-shadows with color-mix().
Activity
  • The author, el-mehdi, has made significant changes across multiple components to improve theming, accessibility, and code quality.
  • The changes include replacing hardcoded color classes with CSS variables, adding ARIA attributes for screen reader support, and fixing code quality issues such as hook violations and deprecated function usage.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an excellent pull request that makes significant improvements across 18 components. The move to CSS variables for theming is a huge win for maintainability. The accessibility fixes for Input and Textarea are crucial, and the type-safety improvements by removing as any are great. The code quality fixes, such as resolving the Rules of Hooks violation and removing dead code, are also well-executed. I've identified a few areas for improvement, primarily related to using Math.random() for generating unique IDs, which can be problematic in React. I've also found a bug in the DialogClose component's asChild implementation that overwrites the child's onClick handler. Overall, this is a high-quality contribution.

Comment thread src/components/Dialog.tsx
Comment on lines +372 to +377
return React.cloneElement(
children as React.ReactElement<{ onClick?: React.MouseEventHandler }>,
{
onClick: handleClick,
}
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

When using the asChild pattern, it's important to merge the component's event handlers with the child's existing handlers, rather than overwriting them. Here, the child's onClick handler is being replaced. This is a bug that will prevent any onClick on the child component from firing.

You should chain the onClick handlers, similar to how it's done in the DialogTrigger and DrawerClose components.

      return React.cloneElement(
        children as React.ReactElement<{ onClick?: React.MouseEventHandler<HTMLButtonElement> }>,
        {
          onClick: (e: React.MouseEvent<HTMLButtonElement>) => {
            handleClick(e);
            (children as React.ReactElement<{ onClick?: React.MouseEventHandler<HTMLButtonElement> }>).props.onClick?.(e);
          },
        }
      );

Comment thread src/components/Checkbox.tsx Outdated
...props
}, ref) => {
const checkboxId = id || `checkbox-${Math.random().toString(36).substr(2, 9)}`;
const checkboxId = id || `checkbox-${Math.random().toString(36).slice(2, 11)}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using Math.random() to generate component IDs is an anti-pattern in React. It can lead to hydration mismatches in server-rendered applications and cause issues with accessibility and state. Please use the React.useId() hook instead for generating stable, unique IDs.

Suggested change
const checkboxId = id || `checkbox-${Math.random().toString(36).slice(2, 11)}`;
const checkboxId = id || React.useId();

Comment thread src/components/FileUpload.tsx Outdated

// Helper function to generate unique ID
const generateId = (): string => Math.random().toString(36).substr(2, 9);
const generateId = (): string => Math.random().toString(36).slice(2, 11);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using Math.random() for generating keys is not recommended as it doesn't guarantee uniqueness, especially if many files are uploaded quickly. A more robust approach would be to use a simple counter to ensure unique IDs within the component's lifecycle. Since this function is at the module level, a module-level counter would be appropriate.

let fileIdCounter = 0;
const generateId = (): string => `file-${fileIdCounter++}`;

This ensures unique IDs for each file added during the session.

Comment thread src/components/Input.tsx Outdated
...props
}, ref) => {
const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
const inputId = id || `input-${Math.random().toString(36).slice(2, 11)}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using Math.random() to generate component IDs is an anti-pattern in React. It can lead to hydration mismatches in server-rendered applications and cause issues with accessibility and state. Please use the React.useId() hook instead for generating stable, unique IDs.

Suggested change
const inputId = id || `input-${Math.random().toString(36).slice(2, 11)}`;
const inputId = id || React.useId();

Comment thread src/components/PasswordInput.tsx Outdated
}, ref) => {
const [showPassword, setShowPassword] = useState(false);
const inputId = id || `password-${Math.random().toString(36).substr(2, 9)}`;
const inputId = id || `password-${Math.random().toString(36).slice(2, 11)}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using Math.random() to generate component IDs is an anti-pattern in React. It can lead to hydration mismatches in server-rendered applications and cause issues with accessibility and state. Please use the React.useId() hook instead for generating stable, unique IDs.

Suggested change
const inputId = id || `password-${Math.random().toString(36).slice(2, 11)}`;
const inputId = id || React.useId();

Comment thread src/components/Textarea.tsx Outdated
...props
}, ref) => {
const textareaId = id || `textarea-${Math.random().toString(36).substr(2, 9)}`;
const textareaId = id || `textarea-${Math.random().toString(36).slice(2, 11)}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using Math.random() to generate component IDs is an anti-pattern in React. It can lead to hydration mismatches in server-rendered applications and cause issues with accessibility and state. Please use the React.useId() hook instead for generating stable, unique IDs.

Suggested change
const textareaId = id || `textarea-${Math.random().toString(36).slice(2, 11)}`;
const textareaId = id || React.useId();

- DialogClose: chain child's onClick with handleClick instead of
  overwriting it (matches DialogTrigger/DrawerClose/CollapsibleTrigger)
- Replace Math.random() ID generation with React.useId() in Checkbox,
  Input, PasswordInput, Textarea for stable IDs across re-renders and
  SSR hydration compatibility
- Replace Math.random() with counter-based ID in FileUpload for
  guaranteed uniqueness
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant