feat: add condensed view for form & input components#147
feat: add condensed view for form & input components#147garrity-miepub merged 52 commits intomainfrom
Conversation
- Add data-slot attributes: input-wrapper, input-label, input, input-error, input-helper - Condensed CSS: reduced padding (0.025rem), heights (sm:1.5rem, md:1.75rem, lg:2rem) - Smaller label (0.75rem), error/helper text (0.6875rem), tighter wrapper gap (0.25rem)
Deploying ui with
|
| Latest commit: |
40b21a0
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://10372951.ui-6d0.pages.dev |
| Branch Preview URL: | https://feature-condensed-view-form.ui-6d0.pages.dev |
There was a problem hiding this comment.
Pull request overview
Adds condensed-density styling support for the Input component by introducing stable data-slot hooks and corresponding body.condensed CSS overrides in the shared stylesheet.
Changes:
- Add
data-slotattributes to Input sub-elements (input-wrapper,input-label,input,input-error,input-helper) to support global density styling. - Add condensed-mode CSS rules in
base.cssto reduce wrapper spacing, input height/padding, and label/helper/error typography.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/styles/base.css | Adds body.condensed rules targeting new Input data-slot hooks for tighter condensed styling. |
| src/components/Input/Input.tsx | Adds data-slot attributes to Input markup to enable density overrides from global CSS. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add data-slot attributes: textarea-wrapper, textarea-label, textarea, textarea-footer, textarea-error, textarea-helper, textarea-count - Condensed CSS: reduced padding (0.025rem), min-heights (sm:2.25rem, md:3rem, lg:3.75rem) - Smaller label (0.75rem), error/helper (0.6875rem), count (0.625rem), tighter gaps
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add data-slot attributes: select-wrapper, select-label, select-trigger, select-dropdown, select-search, select-listbox, select-option, select-group-label, select-error, select-helper - Condensed CSS: trigger heights (sm:1.5rem, md:1.75rem, lg:2rem), compact options, smaller search input - Consistent label/error/helper sizing with Input and Textarea
- Fix sub-pixel padding: 0.025rem → 0.0625rem (1px) for Input, Textarea, Select search - Add per-size font scaling for Input and Textarea (preserves size prop typography) - .text-sm → 0.75rem, .text-base → 0.875rem, .text-lg → 1rem
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Replace single font-size override with per-size scaling (.text-sm/.text-base/.text-lg) - Fix comment: 'reduced height & padding' → 'reduced height & border-radius'
Condensed mode now overrides ALL size variants to one uniform condensed size: - Input: 1.75rem height, 0.8125rem font (regardless of sm/md/lg) - Textarea: 2.25rem min-height, 0.8125rem font (regardless of sm/md/lg) - Select trigger: 1.75rem height, 0.8125rem font (regardless of sm/md/lg) This replaces per-size scaling with a single condensed target, ensuring maximum density and visual consistency. Size prop still works in default mode.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add data-slot='button' attribute - Single condensed size: 1.75rem height, 0.5rem padding, 0.8125rem font, 0.375rem radius - Icon-only variant: 1.75rem square
- Add data-slot attributes to Checkbox and CheckboxGroup components - Checkbox: checkbox-wrapper, checkbox-row, checkbox-indicator, checkbox, checkbox-label, checkbox-description, checkbox-error - CheckboxGroup: checkbox-group, checkbox-group-legend, checkbox-group-description, checkbox-group-items, checkbox-group-error - Add condensed CSS with single-size philosophy (0.875rem square checkbox) - Tighten gaps, reduce label/description/error font sizes
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adjust estimatedDropdownHeight to use 28px per option in condensed mode (vs 40px default) to prevent unnecessary upward-opening when there is sufficient space below.
- Add data-slot attributes to Radio and RadioGroup components - Radio: radio-wrapper, radio-indicator, radio, radio-dot, radio-label, radio-description - RadioGroup: radio-group, radio-group-legend, radio-group-description, radio-group-items, radio-group-error - Add condensed CSS with single-size philosophy (0.875rem circle, 0.375rem dot) - Tighten gaps, reduce label/description/error font sizes
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/components/Button/Button.tsx:127
- The button’s condensed styling relies on
data-slot="button"anddata-size, but{...props}is spread after these attributes. A consumer passingdata-slot/data-size(even unintentionally) will override the internal hooks and break condensed selectors. Consider preventing override by spreadingpropsbefore these attributes and/or omittingdata-slot/data-sizefromButtonProps.
<button
data-slot="button"
data-size={size}
className={cn(buttonVariants({ variant, size, fullWidth }), className)}
ref={ref}
disabled={disabled || isLoading}
aria-busy={isLoading}
{...props}
>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Button: resolve size to 'md' default so data-size is always present - Select: only include helperId in describedByIds when helper is rendered - Textarea: same describedByIds fix as Select
Add data-slot attributes to DateButton, TimeButton, RadioOption, DatePicker, TimePicker, and SchedulePicker components. Add body.condensed CSS overrides in base.css: - Date buttons: compact padding, 0.375rem radius, smaller fonts - Time buttons: 1.75rem height matching Input/Button condensed - Labels: 0.6875rem font, 0.25rem bottom margin - RadioOption: 0.875rem indicator, 0.375rem dot (matches Radio) - All gaps reduced to 0.25rem
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 33 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/components/Button/Button.tsx:131
data-slot/data-sizeare set on the<button>, but{...props}is spread afterwards, so consumers can override those attributes and unintentionally disable condensed styling that targets[data-slot='button']/[data-size='icon']. Consider spreading{...props}before setting these internal attributes (or otherwise preventing overrides).
<button
data-slot="button"
data-size={resolvedSize}
className={cn(
buttonVariants({ variant, size: resolvedSize, fullWidth }),
className
)}
ref={ref}
disabled={disabled || isLoading}
aria-busy={isLoading}
{...props}
>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add data-slot attributes to ServicePicker, ServiceGroupItem, and ServiceItem components. Add body.condensed CSS overrides in base.css: - Card: 0.375rem radius, tighter header/body padding - Heading: 0.875rem font - Group buttons: compact padding, 0.75rem font - Service items: 0.25rem padding, 0.375rem gap - Checkbox/radio: 0.875rem indicators (matches Checkbox/Radio) - Item text: 0.75rem name/price, 0.625rem code/description - Lists: minimal vertical spacing
sm\\:w-32 → sm\:w-32 (single backslash for Tailwind class escaping)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 34 out of 34 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/components/Button/Button.tsx:130
{...props}is spread after internal attributes on this button, so a consumer-provideddata-slot/data-sizecan override the values needed for condensed selectors. Consider moving the props spread earlier or explicitly omittingdata-slot/data-sizefrom forwarded props so these internal hooks can’t be replaced accidentally.
)}
ref={ref}
disabled={disabled || isLoading}
aria-busy={isLoading}
{...props}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add data-slot attributes to Slider sub-elements: slider, slider-label-row, slider-label, slider-value, slider-description, slider-track-wrapper, slider-track, slider-range, slider-thumb, slider-minmax. Add body.condensed CSS overrides in base.css: - Label/value: 0.6875rem font - Description: 0.625rem font - Track: 0.25rem height (collapses to sm) - Thumb: 0.875rem (collapses to sm) - Min/max labels: 0.625rem font - Tighter margins throughout
Helper text element is not rendered when error is present, so aria-describedby was pointing at a non-existent ID. Align with Select/Textarea by adding the !error guard.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 35 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/components/Button/Button.tsx:131
- Because
{...props}is spread afterdata-slot/data-size, a consumer can override those attributes (e.g. passingdata-slot), which can silently break condensed-mode selectors that rely on[data-slot='button']and[data-size='icon']. Consider moving the{...props}spread before these fixed attributes, or explicitly omitting/guardingdata-slot/data-sizefrompropsso the styling hooks stay stable.
<button
data-slot="button"
data-size={resolvedSize}
className={cn(
buttonVariants({ variant, size: resolvedSize, fullWidth }),
className
)}
ref={ref}
disabled={disabled || isLoading}
aria-busy={isLoading}
{...props}
>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Move all body.condensed rules (~3,123 lines) from base.css into src/styles/condensed-view.css for maintainability. base.css imports it via @import at the top so existing consumers are unaffected.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 36 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 36 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Condensed density support for Form & Input components
Adds
data-slotattributes and condensed CSS overrides for form components: Input, Textarea, Select, Button, Checkbox, CheckboxGroup, Radio, RadioGroup, Switch, Address, AdditionalFields, BusinessHoursEditor, CSVColumnMapper, ClaimProviderForm, CountryCodeDropdown, CreateInvoiceModal, CreateReferralModal, DateInput, DateRangePicker, Dropdown, EmployeeForm, EmployerServiceModal, HRISProviderSelector, InviteUserModal, OrderConfirmationWizard, PermissionsEditor, PhoneInput, SchedulePicker, ServicePicker, and Slider.Design Philosophy
Condensed mode uses a single uniform size — all
sizeprop variants (sm/md/lg) collapse to one condensed target. This ensures maximum information density and visual consistency. Thesizeprop continues to work normally in default mode.1.75rem2.25rem0.75rem(12px)0.6875rem(11px)0.375rem0.0625remtop/bottom0.6875rem0.6875rem0.25rem0.875remsquare/circle0.375rem1rem × 1.75rem0.75rem0.75rem0.75rem0.25remChanges
Input —
data-slot:input-wrapper,input-label,input,input-error,input-helperTextarea —
data-slot:textarea-wrapper,textarea-label,textarea,textarea-footer,textarea-error,textarea-helper,textarea-countSelect —
data-slot:select-wrapper,select-label,select-trigger,select-dropdown,select-search,select-listbox,select-option,select-group-label,select-error,select-helperButton —
data-slot:button,data-size: reflects size prop (sm/md/lg/icon)[data-size='icon']instead of.w-10for stabilityCheckbox —
data-slot:checkbox-wrapper,checkbox-row,checkbox-indicator,checkbox,checkbox-label,checkbox-description,checkbox-errorCheckboxGroup —
data-slot:checkbox-group,checkbox-group-legend,checkbox-group-description,checkbox-group-items,checkbox-group-errorRadio —
data-slot:radio-wrapper,radio-indicator,radio,radio-dot,radio-label,radio-descriptionRadioGroup —
data-slot:radio-group,radio-group-legend,radio-group-description,radio-group-items,radio-group-errorSwitch —
data-slot:switch-wrapper,switch,switch-thumb,switch-label,switch-descriptionAddress —
data-slot:address,address-card,address-card-title,address-card-phoneAdditionalFields —
data-slot:additional-fields,additional-fields-title,additional-fields-header,additional-fields-row,additional-fields-contentBusinessHoursEditor —
data-slot:business-hours-editor,business-hours-day,business-hours-day-header,business-hours-day-name,business-hours-closed,business-hours-slots,business-hours-slot-row,business-hours-separatorCSVColumnMapper —
data-slot:csv-mapper,csv-mapper-actions,csv-mapper-alert,csv-mapper-alert-title,csv-mapper-alert-desc,csv-mapper-instructions,csv-mapper-grid,csv-mapper-footer,csv-mapper-progress,csv-card,csv-card-header,csv-card-title,csv-card-body,csv-card-label,csv-card-sample,csv-card-ignoreClaimProviderForm —
data-slot:claim-form-title,claim-form-provider,claim-form-provider-name,claim-form-provider-addr,claim-form-body,claim-form-error,claim-form-error-text,claim-form-section,claim-form-section-title,claim-form-grid,claim-form-terms,claim-form-terms-label,claim-form-terms-text,claim-form-actions:has()CountryCodeDropdown —
data-slot:country-dropdown,country-dropdown-trigger,country-dropdown-flag,country-dropdown-dialcode,country-dropdown-chevron,country-dropdown-panel,country-dropdown-search,country-dropdown-search-input,country-dropdown-list,country-dropdown-option,country-dropdown-option-flag,country-dropdown-option-name,country-dropdown-option-dialcodeCreateInvoiceModal —
data-slot:invoice-modal,invoice-modal-header,invoice-modal-error,invoice-modal-step-desc,invoice-modal-toggle-all,invoice-modal-empty,invoice-modal-empty-icon,invoice-modal-order-list,invoice-modal-order,invoice-modal-order-check,invoice-modal-order-number,invoice-modal-order-amount,invoice-modal-order-detail,invoice-modal-summary,invoice-modal-summary-total,invoice-modal-footer,invoice-modal-footer-buttonsCreateReferralModal —
data-slot:referral-modal,referral-modal-error,referral-modal-employee,referral-modal-employee-label,referral-modal-employee-name,referral-modal-employee-detail,referral-modal-section-label,referral-modal-service-list,referral-modal-service,referral-modal-service-check,referral-modal-service-name,referral-modal-service-desc,referral-modal-service-price,referral-modal-priority-row,referral-modal-priority,referral-modal-summary,referral-modal-summary-totalDateInput — condensed support via delegation to Input component (non-calendar mode) and dedicated condensed overrides for calendar popup mode
DateRangePicker —
data-slot:date-range-picker,date-range-trigger,date-range-trigger-icon,date-range-popup,date-range-presets,date-range-preset,date-range-calendar,date-range-subtitle,date-range-calendars,date-range-month-header,date-range-month-label,date-range-nav,date-range-grid,date-range-weekdays,date-range-weekday,date-range-day-grid,date-range-day,date-range-mobile-overlay,date-range-mobile-sheet,date-range-mobile-handle,date-range-mobile-title,date-range-mobile-done,date-range-filterDropdown —
data-slot:dropdown-menu,dropdown-search,dropdown-search-input,dropdown-select-all,dropdown-empty,dropdown-header,dropdown-header-row,dropdown-header-title,dropdown-header-subtitle,dropdown-content,dropdown-item,dropdown-item-icon,dropdown-item-label,dropdown-checkbox,dropdown-label,dropdown-separatorEmployeeForm —
data-slot:employee-form,employee-form-section,employee-form-section-title,employee-form-grid,employee-form-address,employee-form-phones,employee-form-phone-row,employee-form-phone-remove,employee-form-add-phone,employee-form-status,employee-form-status-label,employee-form-footer,employee-form-invite,employee-form-invite-label,employee-form-actions,employee-form-cancel,employee-form-submitEmployerServiceModal —
data-slot:employer-service-body,employer-service-error,employer-service-info,employer-service-info-name,employer-service-info-price,employer-service-section,employer-service-section-title,employer-service-switch-row,employer-service-switch-label,employer-service-indent,employer-service-label,employer-service-price-input,employer-service-price-prefix,employer-service-textarea,employer-service-spinnerHRISProviderSelector —
data-slot:hris-connected-card,hris-connected-row,hris-connected-logo,hris-connected-title,hris-connected-desc,hris-connected-sync,hris-connected-alert,hris-connected-actions,hris-connected-btn,hris-connected-btn-primary,hris-search-wrapper,hris-search-icon,hris-search-input,hris-grid,hris-card,hris-card-icon,hris-card-label,hris-no-resultsInviteUserModal —
data-slot:invite-user-body,invite-user-entity,invite-user-success,invite-user-error,invite-user-name-grid,invite-user-message,invite-user-label,invite-user-textarea,invite-user-info,invite-user-spinnerOrderConfirmationWizard —
data-slot:ocw-root,ocw-progress,ocw-step-circle,ocw-step-label,ocw-step-connector,ocw-summary-card,ocw-summary-row,ocw-summary-title,ocw-summary-subtitle,ocw-step-card,ocw-step-content,ocw-step-body,ocw-step-title,ocw-step-desc,ocw-detail-grid,ocw-checkbox-row,ocw-checkbox,ocw-checkbox-label,ocw-label,ocw-textarea,ocw-checkbox-card,ocw-checkbox-card-title,ocw-checkbox-card-desc,ocw-confirm-list,ocw-confirm-row,ocw-confirm-label,ocw-navPermissionsEditor —
data-slot:perm-editor,perm-section,perm-section-header,perm-section-title,perm-section-username,perm-groups,perm-group-header,perm-group-name,perm-group-list,perm-item,perm-item-row,perm-item-toggle,perm-item-label,perm-separator,perm-employer-section,perm-employer-list,perm-employer-row,perm-employer-label,perm-employer-name,perm-employer-addr,perm-summary,perm-summary-title,perm-summary-list,perm-summary-subtitlePhoneInput —
data-slot:phone-input,phone-row,phone-number-wrapper,phone-number-label,phone-number-input,phone-type-select,phone-action,phone-add-btn,phone-remove-btnSchedulePicker —
data-slot:schedule-picker,schedule-date-btn,schedule-date-weekday,schedule-date-day,schedule-date-month,schedule-time-btn,schedule-radio,schedule-radio-indicator,schedule-radio-dot,schedule-radio-title,schedule-radio-desc,schedule-date-picker,schedule-date-picker-label,schedule-date-picker-list,schedule-time-picker,schedule-time-picker-label,schedule-time-picker-gridServicePicker —
data-slot:service-picker,service-picker-header,service-picker-heading,service-picker-search,service-picker-body,service-picker-error,service-picker-loading,service-picker-list,service-picker-empty,service-picker-group-btn,service-picker-group-name,service-picker-group-dot,service-picker-group-list,service-picker-item,service-picker-item-check,service-picker-radio,service-picker-radio-dot,service-picker-item-name,service-picker-item-code,service-picker-item-desc,service-picker-item-priceSlider —
data-slot:slider,slider-label-row,slider-label,slider-value,slider-description,slider-track-wrapper,slider-track,slider-range,slider-thumb,slider-minmaxCSS Architecture
All condensed CSS rules are extracted into a dedicated file
src/styles/condensed-view.cssand imported via@import './condensed-view.css'at the top ofbase.css.Font-size scale fix
Reduced the condensed font-size scale across all components to ensure condensed text is always visibly smaller than standard mode:
0.8125rem→0.75rem(13px → 12px)0.8125rem→0.6875rem(13px → 11px) — fixes bug where condensed was larger than standardtext-xs0.75rem→0.6875rem(12px → 11px)Notes
!importantis required — Tailwind 4@layer utilitieslowers specificity below evenbody.condensed [data-slot='x']selectors (confirmed in PR feat: add condensed view for text & data display components #146)body.condensed— zero impact on default densitydefaultValueinstead ofvalueto resolve React controlled-component warningtransform: translateX(...)to preservetransition-transformanimation[data-size='icon']instead of.w-10class matchmargin-bottomon children instead ofgap(wrapper usesspace-y-*block layout, not flex/grid)data-slotinstead of.flexdescendant selector to avoid broad matchingdata-slotattrs instead of fragilenth-childpositional selectorsshowPrint/onPrint/showExport/onExport) and invalid label keys (thisYear/lastYear)data-slotfrom Button component instances (EmployerServiceModal, InviteUserModal) to preserve Button's internaldata-slot="button"needed for condensed stylingaria-describedbyno longer references helper text ID when error is shown (matches Select/Textarea pattern)icon-csv.svgimage reference with Font Awesomefa-file-csvicon in HRISProviderSelector