-
Notifications
You must be signed in to change notification settings - Fork 13.4k
style(textarea): adjusting min-height of the element #30950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Updates ion-textarea styling so the control’s minimum height better matches the configured rows (especially for rows="1"), with additional Ionic-theme-specific selector and spacing fixes.
Changes:
- Add a
data-attr-rowsattribute to the textarea inner wrapper to enable rows-based styling. - Update Ionic theme sizing/min-height behavior, plus tighten
auto-growselectors to excludeauto-grow="false". - Adjust Ionic theme textarea top margin behavior (and remove the old outline-only margin rule).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| core/src/components/textarea/textarea.tsx | Adds data-attr-rows on the wrapper for CSS-based rows sizing. |
| core/src/components/textarea/textarea.ionic.scss | Reworks Ionic theme min-height logic, auto-grow selectors, and label-placement spacing. |
| core/src/components/textarea/textarea.ionic.outline.scss | Removes outline-only textarea margin-top rule (spacing now handled elsewhere). |
| core/src/components/textarea/textarea.common.scss | Adds rows-related min-height override for certain host/class combinations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Setting height to 0 allows it to collapse in height | ||
| // instead of growing above the min height by default. | ||
| .textarea-wrapper-inner { | ||
| height: 0; | ||
| } | ||
|
|
||
| :host(.textarea-size-small) .textarea-wrapper-inner { | ||
| --padding-top: #{globals.$ion-space-200}; | ||
| --padding-end: #{globals.$ion-space-300}; | ||
| --padding-bottom: #{globals.$ion-space-200}; | ||
| --padding-start: #{globals.$ion-space-300}; | ||
|
|
||
| min-height: globals.$ion-scale-2800; | ||
| } | ||
|
|
||
| :host(.textarea-size-medium) .textarea-wrapper-inner { | ||
| --padding-top: #{globals.$ion-space-300}; | ||
| --padding-end: #{globals.$ion-space-400}; | ||
| --padding-bottom: #{globals.$ion-space-300}; | ||
| --padding-start: #{globals.$ion-space-400}; | ||
|
|
||
| min-height: globals.$ion-scale-3400; | ||
| } | ||
|
|
||
| :host(.textarea-size-large) .textarea-wrapper-inner { | ||
| --padding-top: #{globals.$ion-space-400}; | ||
| --padding-end: #{globals.$ion-space-500}; | ||
| --padding-bottom: #{globals.$ion-space-400}; | ||
| --padding-start: #{globals.$ion-space-500}; | ||
|
|
||
| min-height: globals.$ion-scale-3600; | ||
| } | ||
|
|
||
| // When the rows attribute is set, the container of the textarea should | ||
| // should increase in height to accommodate the number of rows. | ||
| // The min-height should accomodate the number of rows, plus a little extra | ||
| // so that the next line is partially visible. To that we add the amount of | ||
| // the bottom padding and the border width outside the textarea. | ||
| // If --host-rows is set, then it will be used, otherwise the data-attr-rows | ||
| // attribute will be used. | ||
| .textarea-wrapper-inner[data-attr-rows] { | ||
| --number-rows: var(--host-rows, attr(data-attr-rows number)); | ||
| min-height: calc(var(--number-rows) * 1.2em + 1.5em + var(--padding-bottom, 0px) + var(--border-width, 0px)); | ||
| } |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
textarea-wrapper-inner is forced to height: 0, but after removing the size-specific min-height rules the element only gets a min-height when data-attr-rows is present. If rows is not set (common case), the wrapper can collapse and the textarea may overflow/overlap other content. Provide a non-rows fallback min-height (or ensure the rows-based min-height logic always applies, including default rows).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be ignored.
| <label class="textarea-wrapper" htmlFor={inputId} onClick={this.onLabelClick} part="wrapper"> | ||
| {this.renderLabelContainer()} | ||
| <div class="textarea-wrapper-inner"> | ||
| <div class="textarea-wrapper-inner" data-attr-rows={this.rows !== undefined ? this.rows.toString() : undefined}> |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data-attr-rows is only set when this.rows is defined, but the updated Ionic theme styling relies on that attribute to compute min-height. This can break the default textarea (no rows prop/attr) in the Ionic theme. Consider always providing a rows value for styling purposes (e.g., defaulting to the native textarea default) or using a CSS custom property instead of a conditional attribute.
| <div class="textarea-wrapper-inner" data-attr-rows={this.rows !== undefined ? this.rows.toString() : undefined}> | |
| <div class="textarea-wrapper-inner" data-attr-rows={(this.rows ?? 2).toString()}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be ignored.
| // If --host-rows is set, then it will be used, otherwise the data-attr-rows | ||
| // attribute will be used. | ||
| .textarea-wrapper-inner[data-attr-rows] { | ||
| --number-rows: var(--host-rows, attr(data-attr-rows number)); | ||
| min-height: calc(var(--number-rows) * 1.2em + 1.5em + var(--padding-bottom, 0px) + var(--border-width, 0px)); |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attr(data-attr-rows number) in a calc() for layout is not reliably supported across browsers. If unsupported, the computed min-height becomes invalid and the textarea can collapse (especially with height: 0 on the wrapper). Prefer setting a numeric CSS custom property from TS/JS (or another supported mechanism) instead of relying on attr() for numeric values in properties.
| // If --host-rows is set, then it will be used, otherwise the data-attr-rows | |
| // attribute will be used. | |
| .textarea-wrapper-inner[data-attr-rows] { | |
| --number-rows: var(--host-rows, attr(data-attr-rows number)); | |
| min-height: calc(var(--number-rows) * 1.2em + 1.5em + var(--padding-bottom, 0px) + var(--border-width, 0px)); | |
| // The --host-rows custom property should be set (for example from JS/TS) | |
| // to reflect the number of rows; we fall back to 1 row if it is not set. | |
| .textarea-wrapper-inner[data-attr-rows] { | |
| min-height: calc(var(--host-rows, 1) * 1.2em + 1.5em + var(--padding-bottom, 0px) + var(--border-width, 0px)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be ignored. Has this suggestion of implementation was intended. In the future we can remove the usage of the variable --host-rows.
| * there is no need to add margin-top to the textarea. | ||
| */ | ||
| :host(.textarea-label-placement-stacked) textarea, | ||
| :host(.textarea-label-placement-floating) textarea { | ||
| margin-top: 0; |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Ionic theme you reset margin-top on the textarea for stacked/floating labels, but the auto-grow sizing uses .native-wrapper::after (in textarea.common.scss) to mirror textarea spacing. If the pseudo-element keeps the old top margin while the textarea margin is zero, auto-grow height calculations can become inconsistent. Consider overriding the corresponding .native-wrapper::after margin for these label placements in the Ionic theme too (or otherwise keeping textarea and pseudo-element spacing in sync).
| * there is no need to add margin-top to the textarea. | |
| */ | |
| :host(.textarea-label-placement-stacked) textarea, | |
| :host(.textarea-label-placement-floating) textarea { | |
| margin-top: 0; | |
| * there is no need to add margin-top to the textarea. | |
| * We also need to keep the auto-grow pseudo-element spacing | |
| * in sync with the textarea spacing. | |
| */ | |
| :host(.textarea-label-placement-stacked) textarea, | |
| :host(.textarea-label-placement-floating) textarea { | |
| margin-top: 0; | |
| } | |
| :host(.textarea-label-placement-stacked) .native-wrapper::after, | |
| :host(.textarea-label-placement-floating) .native-wrapper::after { | |
| margin-top: 0; |
| * but rather respect the natural height of the textarea. Specially when | ||
| * rows is set to 1. | ||
| */ | ||
| :host(.textarea-fill-outline.sc-ion-textarea-md-h[rows]), |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Selector uses generated scope class .sc-ion-textarea-md-h, which is brittle and atypical compared to other styles that key off the stable .md / .ios host classes. Prefer targeting the mode via .md (or another stable mode indicator) instead of the generated scope class so the rule does not depend on Stencil scoping internals.
| :host(.textarea-fill-outline.sc-ion-textarea-md-h[rows]), | |
| :host(.textarea-fill-outline.md[rows]), |
| // The height should be auto only when auto-grow is enabled. | ||
| :host([auto-grow]) .textarea-wrapper-inner { | ||
| :host([auto-grow]:not([auto-grow="false"])) .textarea-wrapper-inner { | ||
| height: auto; |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR description says the :host([auto-grow]) .textarea-wrapper-inner selector should apply only when auto-grow = false, but the updated selector applies when auto-grow is enabled ([auto-grow] and not [auto-grow="false"]). If the description is meant to say “only when auto-grow is enabled”, consider correcting the PR description to avoid confusion.
| // The height should be auto only when auto-grow is enabled. | ||
| :host([auto-grow]) .textarea-wrapper-inner { | ||
| :host([auto-grow]:not([auto-grow="false"])) .textarea-wrapper-inner { | ||
| height: auto; | ||
| } | ||
|
|
||
| // The min and max height should be inherited if auto-grow is enabled. | ||
| // This allows the textarea to grow and shrink as needed. | ||
| :host([auto-grow]) .native-wrapper { | ||
| :host([auto-grow]:not([auto-grow="false"])) .native-wrapper { | ||
| min-height: inherit; |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Ionic theme selectors were updated to exclude auto-grow="false", but textarea.common.scss still has multiple :host([auto-grow]) ... rules. If the Stencil boolean prop parses auto-grow="false" as false, those common styles will still apply because the attribute is present, causing inconsistencies across themes. Consider updating the common auto-grow selectors to also exclude [auto-grow="false"] for consistent behavior.
| * but rather respect the natural height of the textarea, especially when | ||
| * rows is set to 1. | ||
| */ | ||
| :host(.textarea-fill-outline.sc-ion-textarea-md-h[rows]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As Copolit mentioned, we shouldn't be targeting generated classes. This should be moved into the textarea.md.outline.scss file since it's for the md styles.
| * Clamp the minimum value to 1 to prevent 0 or negative heights. | ||
| * Add 0.5 to show a half-line peek at the next row. | ||
| */ | ||
| --number-rows: calc(max(var(--host-rows, 1), 1) + 0.5); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to accomplish the styling without creating new CSS variables?
| })} | ||
| // For ionic theme, we need to define this css variable | ||
| style={ | ||
| theme === 'ionic' ? { '--host-rows': this.rows !== undefined ? this.rows.toString() : undefined } : undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to accomplish the styling without creating new CSS variables?
Issue number: resolves internal
What is the current behavior?
The

heightof the textarea would not correspond to the rows number, when it the row number was set to1:Ionic theme specifics:
:host([auto-grow]) .textarea-wrapper-innerThis selector was always being applied independently on the value of the attribute
auto-grow;What is the new behavior?
The

min-heightis now respecting the rows attribute:The ionic theme has the following changes:
.textarea-size-*classes stopped forcing themin-height;This will cause the last line to be partially visible;
:host([auto-grow]) .textarea-wrapper-inner, to be applied only whenauto-grow = false;--host-rowsin the host, so that the minimum height can be calculated in the elementdiv[.textarea-wrapper-inner];Does this introduce a breaking change?
Other information