Skip to content

Conversation

@GoldGroove06
Copy link
Collaborator

@GoldGroove06 GoldGroove06 commented Dec 21, 2025

Summary by CodeRabbit

  • New Features
    • TextArea component now supports color and radius customization APIs.
    • Added placeholder, disabled, and readonly props for enhanced control.
    • Introduced multiple styling variants (soft, outline, solid, ghost), size options, and resize controls.
    • Enhanced visual styling with improved scrollbar appearance and focus states.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Dec 21, 2025

🦋 Changeset detected

Latest commit: c80a283

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@radui/ui Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 21, 2025

Walkthrough

This PR enhances the TextArea component with color and radius API support. It extends TextAreaProps with new properties (placeholder, disabled, readonly), integrates TextAreaRootProps and TextAreaInputProps types, adds data-attribute-driven styling modifiers for resize/radius/size/variant options, and includes new story variants demonstrating the updated functionality.

Changes

Cohort / File(s) Summary
Changeset
.changeset/rude-forks-admire.md
Documents minor release for TextArea color and radius API additions with new styling.
TextArea Component Props & Types
src/components/ui/TextArea/TextArea.tsx, src/components/ui/TextArea/fragments/TextAreaRoot.tsx
Extended TextAreaProps to include TextAreaRootProps and TextAreaInputProps; added public props (placeholder, disabled, readonly); updated TextAreaRoot to accept color and radius parameters with data-attribute generation via useCreateDataAccentColorAttribute and useComposeAttributes.
TextArea Stories
src/components/ui/TextArea/stories/TextArea.stories.tsx
Added four new exported story variants (Size, Variant, Resize, TextAreaRadius) with corresponding constants and mapping; extended base Template with disabled and readonly instances.
TextArea Styling
styles/themes/components/textarea.scss
Consolidated and expanded textarea styling with explicit block-level display, full width, and box-sizing; introduced data-attribute-based modifiers for resize (horizontal/vertical/none/both), radius (none/small/medium/large/full), size (small/medium/large), and variant (soft/outline/solid/ghost); added placeholder, focus, and webkit scrollbar styling; preserved disabled/readonly states.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas requiring attention:
    • Verify proper type integration: confirm that TextAreaRootProps and TextAreaInputProps are correctly extended and that new props (color, radius, placeholder, disabled, readonly) flow correctly through the component hierarchy.
    • Data-attribute consistency: validate that all data-attribute selectors in SCSS (data-textarea-resize, data-textarea-radius, data-textarea-size, data-textarea-variant) match the component's attribute generation.
    • Story completeness: ensure the new story variants adequately demonstrate the color, radius, resize, and size features.

Possibly related PRs

Suggested reviewers

  • kotAPI

Poem

🐰 wiggles nose with delight
A TextArea blooms with colors so bright,
With radius curves and placeholders deep,
New styling modifiers make features to keep—
Size, variant, resize—what a wonderful sight! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'textArea updated' is vague and generic, providing no meaningful information about the specific changes made to the TextArea component. Use a more descriptive title that highlights the main changes, such as 'Add color and radius API support to TextArea component' or 'TextArea: Add styling customization and new props'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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: 4

Caution

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

⚠️ Outside diff range comments (1)
src/components/ui/TextArea/TextArea.tsx (1)

20-25: Remove duplicate children rendering.

Children are rendered twice: once inside TextAreaInput (lines 22-23) and again after it (line 24). Since TextAreaInput wraps an HTML <textarea> element, which doesn't support children (textareas use the value prop), this causes incorrect rendering behavior.

🔎 Proposed fix
 const TextArea = React.forwardRef<React.ElementRef<'div'>, TextAreaProps>(({ customRootClass = '', placeholder = '', className = '', disabled = false, readonly = false, children, ...props }, ref) => {
     return (
         <TextAreaRoot ref={ref} customRootClass={customRootClass} className={clsx(className)} {...props}>
-            <TextAreaInput placeholder={placeholder} disabled={disabled} readOnly={readonly}>
-                {children}
-            </TextAreaInput>
-            {children}
+            <TextAreaInput placeholder={placeholder} disabled={disabled} readOnly={readonly} />
         </TextAreaRoot>
     );
 }) as TextAreaComponent;

Additionally, consider removing children from the component signature entirely, as textarea components shouldn't accept children:

-const TextArea = React.forwardRef<React.ElementRef<'div'>, TextAreaProps>(({ customRootClass = '', placeholder = '', className = '', disabled = false, readonly = false, children, ...props }, ref) => {
+const TextArea = React.forwardRef<React.ElementRef<'div'>, TextAreaProps>(({ customRootClass = '', placeholder = '', className = '', disabled = false, readonly = false, ...props }, ref) => {
     return (
         <TextAreaRoot ref={ref} customRootClass={customRootClass} className={clsx(className)} {...props}>
             <TextAreaInput placeholder={placeholder} disabled={disabled} readOnly={readonly} />
         </TextAreaRoot>
     );
 }) as TextAreaComponent;
🧹 Nitpick comments (1)
styles/themes/components/textarea.scss (1)

7-7: Consider aligning the default resize behavior with the component prop.

The CSS default is resize: none, but TextAreaRoot.tsx (line 18) defaults to resize='both'. While the component prop will override the CSS, aligning these defaults would improve clarity and consistency.

Either:

  1. Change the CSS default to match the component:
-  resize: none;
+  resize: both;

Or:
2. Change the component default to match the CSS in TextAreaRoot.tsx:

-({ children, customRootClass = '', className = '', variant = '', size = '', resize = 'both', color = '', radius = '', ...props }, ref) => {
+({ children, customRootClass = '', className = '', variant = '', size = '', resize = 'none', color = '', radius = '', ...props }, ref) => {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 140d24d and c80a283.

📒 Files selected for processing (5)
  • .changeset/rude-forks-admire.md (1 hunks)
  • src/components/ui/TextArea/TextArea.tsx (1 hunks)
  • src/components/ui/TextArea/fragments/TextAreaRoot.tsx (2 hunks)
  • src/components/ui/TextArea/stories/TextArea.stories.tsx (2 hunks)
  • styles/themes/components/textarea.scss (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-12-12T08:34:33.079Z
Learnt from: decipher-cs
Repo: rad-ui/ui PR: 417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.

Applied to files:

  • src/components/ui/TextArea/stories/TextArea.stories.tsx
🧬 Code graph analysis (3)
src/components/ui/TextArea/stories/TextArea.stories.tsx (1)
src/components/ui/Radio/stories/Radio.stories.tsx (2)
  • Variants (44-65)
  • Sizes (67-92)
src/components/ui/TextArea/fragments/TextAreaRoot.tsx (1)
src/core/hooks/createDataAttribute/index.ts (3)
  • useCreateDataAttribute (10-25)
  • useCreateDataAccentColorAttribute (48-55)
  • useComposeAttributes (33-40)
src/components/ui/TextArea/TextArea.tsx (2)
src/components/ui/TextArea/fragments/TextAreaRoot.tsx (1)
  • TextAreaRootProps (8-15)
src/components/ui/TextArea/fragments/TextAreaInput.tsx (1)
  • TextAreaInputProps (3-3)
🪛 GitHub Actions: Lint
styles/themes/components/textarea.scss

[error] 133-133: no-descending-specificity: Expected selector ".rad-ui-text-area textarea:disabled" to come before selector ".rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus".


[error] 134-134: no-descending-specificity: Expected selector ".rad-ui-text-area textarea:read-only" to come before selector ".rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus".

⏰ 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: coverage
🔇 Additional comments (5)
.changeset/rude-forks-admire.md (1)

1-5: LGTM!

The changeset correctly documents the addition of color and radius API support in TextArea as a minor release.

src/components/ui/TextArea/stories/TextArea.stories.tsx (1)

36-66: LGTM!

The new story variants (Size, Variant, Resize, TextAreaRadius) follow a consistent pattern and effectively demonstrate the new TextArea props.

src/components/ui/TextArea/fragments/TextAreaRoot.tsx (1)

4-25: LGTM!

The integration of color and radius props is well-implemented. The use of useComposeAttributes to merge base data attributes with accent color attributes follows the established pattern and correctly generates the necessary data attributes for styling.

styles/themes/components/textarea.scss (1)

1-122: LGTM!

The data-attribute-driven styling approach is well-structured and provides comprehensive coverage for all TextArea variants, sizes, radii, and resize options. The base styles, focus states, and scrollbar customization enhance the user experience.

src/components/ui/TextArea/TextArea.tsx (1)

4-7: LGTM!

The prop composition pattern correctly extends TextAreaProps with TextAreaRootProps and TextAreaInputProps, allowing the TextArea component to accept all props from its fragments.

Comment on lines +5 to +8
const Variants = ['solid', 'soft', 'outline', 'ghost'];
const Sizes = ['small', 'medium', 'large'];
const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both' | undefined> = ['none', 'vertical', 'horizontal', 'both', undefined ];
const Radius = ['none', 'small', 'medium', 'large'];
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove undefined from the Resizes array or handle it separately.

Including undefined in the Resizes array will cause a React key warning when the array is mapped in the Resize story (line 55), since undefined cannot be used as a valid key.

🔎 Proposed fix
-const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both' | undefined> = ['none', 'vertical', 'horizontal', 'both', undefined ];
+const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both'> = ['none', 'vertical', 'horizontal', 'both'];

If you need to test the default/undefined behavior, create a separate story instance rather than including it in the mapped array.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Variants = ['solid', 'soft', 'outline', 'ghost'];
const Sizes = ['small', 'medium', 'large'];
const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both' | undefined> = ['none', 'vertical', 'horizontal', 'both', undefined ];
const Radius = ['none', 'small', 'medium', 'large'];
const Variants = ['solid', 'soft', 'outline', 'ghost'];
const Sizes = ['small', 'medium', 'large'];
const Resizes: Array<'none' | 'vertical' | 'horizontal' | 'both'> = ['none', 'vertical', 'horizontal', 'both'];
const Radius = ['none', 'small', 'medium', 'large'];
🤖 Prompt for AI Agents
In src/components/ui/TextArea/stories/TextArea.stories.tsx around lines 5 to 8,
the Resizes array includes undefined which will produce React key warnings when
mapped; remove undefined from the Resizes array and, if you need to demonstrate
the default/undefined behavior, create a separate story instance (or add a
specific case) that renders the TextArea without a resize prop instead of
including undefined in the mapped list.

<TextArea disabled placeholder='this textarea is disabled'/>


<TextArea readonly className='w-10' placeholder='this textarea is readonly'/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Verify the className 'w-10' for the readonly textarea.

The className='w-10' applies a width of 2.5rem (40px), which is extremely narrow for a textarea. This appears to be a typo.

Consider removing the className or using w-full for full width:

🔎 Proposed fix
-        <TextArea readonly className='w-10' placeholder='this textarea is readonly'/>
+        <TextArea readonly placeholder='this textarea is readonly'/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TextArea readonly className='w-10' placeholder='this textarea is readonly'/>
<TextArea readonly placeholder='this textarea is readonly'/>
🤖 Prompt for AI Agents
In src/components/ui/TextArea/stories/TextArea.stories.tsx around line 18, the
readonly TextArea is given className='w-10' which sets a very narrow width
(2.5rem) likely by typo; remove the className or replace it with a more
appropriate utility such as 'w-full' (or no className) so the readonly textarea
renders at a usable width.

Comment on lines +9 to +10
readonly ?: boolean;
disabled ?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove extra spaces before the optional type indicators.

The readonly and disabled props have unnecessary spaces before the ? operator.

🔎 Proposed fix
-    readonly ?: boolean;
-    disabled ?: boolean;
+    readonly?: boolean;
+    disabled?: boolean;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
readonly ?: boolean;
disabled ?: boolean;
readonly?: boolean;
disabled?: boolean;
🤖 Prompt for AI Agents
In src/components/ui/TextArea/TextArea.tsx around lines 9 to 10, the prop
declarations use an extra space before the optional operator (`readonly ?:
boolean;` and `disabled ?: boolean;`); remove the space so the declarations read
`readonly?: boolean;` and `disabled?: boolean;` to match TypeScript syntax and
project linting.

Comment on lines +133 to 143
.rad-ui-text-area textarea:disabled,
.rad-ui-text-area textarea:read-only {
background-color: var(--rad-ui-color-accent-200);
color: var(--rad-ui-color-accent-600);
cursor: not-allowed;
}

.rad-ui-text-area textarea:disabled::placeholder,
.rad-ui-text-area textarea:read-only::placeholder {
color: var(--rad-ui-color-accent-500);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix CSS specificity ordering to resolve pipeline failures.

The linter reports that .rad-ui-text-area textarea:disabled and .rad-ui-text-area textarea:read-only selectors should come before .rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus to maintain proper specificity ordering.

🔎 Proposed fix

Move the disabled and read-only styles before the ghost variant styles:

 .rad-ui-text-area[data-textarea-variant="solid"] textarea {
   background-color: var(--rad-ui-color-accent-100);
   border: 2px solid var(--rad-ui-color-accent-400);
 }

+.rad-ui-text-area textarea:disabled,
+.rad-ui-text-area textarea:read-only {
+  background-color: var(--rad-ui-color-accent-200);
+  color: var(--rad-ui-color-accent-600);
+  cursor: not-allowed;
+}
+
+.rad-ui-text-area textarea:disabled::placeholder,
+.rad-ui-text-area textarea:read-only::placeholder {
+  color: var(--rad-ui-color-accent-500);
+}

 .rad-ui-text-area[data-textarea-variant="ghost"] textarea {
   background-color: transparent;
   border: 2px solid transparent;
 }

 .rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus {
   border-color: var(--rad-ui-color-accent-500);
 }

-.rad-ui-text-area textarea:disabled,
-.rad-ui-text-area textarea:read-only {
-  background-color: var(--rad-ui-color-accent-200);
-  color: var(--rad-ui-color-accent-600);
-  cursor: not-allowed;
-}
-
-.rad-ui-text-area textarea:disabled::placeholder,
-.rad-ui-text-area textarea:read-only::placeholder {
-  color: var(--rad-ui-color-accent-500);
-}

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Actions: Lint

[error] 133-133: no-descending-specificity: Expected selector ".rad-ui-text-area textarea:disabled" to come before selector ".rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus".


[error] 134-134: no-descending-specificity: Expected selector ".rad-ui-text-area textarea:read-only" to come before selector ".rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus".

🤖 Prompt for AI Agents
In styles/themes/components/textarea.scss around lines 133 to 143, the
disabled/read-only selector block is placed after higher-specificity
ghost-variant focus rules which breaks the linter's specificity ordering; move
the entire block for `.rad-ui-text-area textarea:disabled, .rad-ui-text-area
textarea:read-only { ... }` and its placeholder rules immediately before the
`.rad-ui-text-area[data-textarea-variant="ghost"] textarea:focus` rules
(preserving all property names and values) so disabled/read-only rules appear
earlier than the ghost-variant focus selectors to satisfy specificity ordering.

@kotAPI kotAPI merged commit bdf63a1 into rad-ui:main Dec 24, 2025
4 of 7 checks passed
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.

2 participants