refactor: inline Tailwind, remove SCSS style blocks, and unify class override API#489
Merged
kelsos merged 58 commits intorotki:mainfrom Mar 28, 2026
Merged
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #489 +/- ##
==========================================
+ Coverage 83.79% 85.01% +1.21%
==========================================
Files 110 139 +29
Lines 4820 5024 +204
Branches 1448 1509 +61
==========================================
+ Hits 4039 4271 +232
+ Misses 781 753 -28 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
52da413 to
7179d84
Compare
3568033 to
cf0f8a0
Compare
- Replace getIconColorClass() with tv() color variant - Add ui computed wrapper for template binding - Type components computed with SvgComponent tuple alias - Delete composables/colors/icon.ts (inlined into tv())
…onfig
- Replace SCSS style block with tv({ slots }) for all progress elements
- Move keyframe animations (buffer-pulse, slide-rail, collapse-stroke) to Tailwind config
- Add svg/circle slots with variant dimension for circular animation classes
- Replace v-bind() CSS with computed :style bindings for dynamic values
- Extract ProgressVariant as const enum in progress-props.ts
- Use compoundVariants for circular vs linear label positioning
- Nest circular label inside circularContainer for proper centering
- Extract clampPercent helper, CIRCLE_RADIUS/CIRCLE_CIRCUMFERENCE constants
- Extend all 6 context colors (original only had primary/secondary)
- Add Colors and CircularColors stories
- Update e2e tests (progress, footer-stepper, data-table) for data attributes
… active
- Replace SCSS style block with tv({ slots }) for wrapper/inner/input/toggle/label
- Use group/switch for hover shadow on toggle, peer-active for ripple
- Use compoundVariants for checked × disabled × color combinations
- Handle dark mode track/toggle colors in compound variants
- Use !important for disabled and validation overrides
- Add instanceof guard replacing type cast in input handler
- Use useFormTextDetail validation computed
- Add data-disabled, data-checked, data-error attributes
- Delete composables/colors/switch.ts (inlined into tv())
- Update tests for inline Tailwind class assertions
- Replace SCSS style block with tv({ slots }) for root/prepend/label/close/closeIcon
- Use compoundVariants for color × variant × clickable × disabled combinations
- Use compoundSlots for shared dark text on filled context colors
- Extract ChipSize, ChipVariant, CHIP_CLOSE_ICON_SIZES to chip-props.ts
- Add data-variant, data-color, data-disabled attributes
- Delete composables/colors/chip.ts (inlined into tv())
- Update tests for inline Tailwind class and data attribute assertions
- Replace plain <style> block with tv() bordered variant - Use dark: prefix instead of manual .dark class toggle - Simplify watch([selectedDate]) to watch(selectedDate) - Update tests to check Tailwind classes instead of .rui-calendar selector
… utilities
- Replace SCSS module with single tv() definition following Nuxt UI pattern
- Use ui.slot({ class: ... }) overrides for per-column alignment/sort classes
- Extract TableAlign, SortDirection as const enums to table-props.ts
- Extract getAlignClass(), getSortButtonAlignClass() shared utilities
- Use position variant (default/sticky/fixed) instead of conflicting absolute+!fixed
- Fix duplicate alignment classes on th elements (text-left appeared twice)
- Fix sticky header flicker: single-frame width sync instead of double-RAF clear+apply
- Remove unused clearColumnWidths, simplify syncRafId
- Add data-sorted, data-direction attributes on sort buttons
- Update e2e to check Tailwind classes instead of CSS module selectors
…component
- Replace SCSS module with tv() in data-table-styles.ts
- Extract RuiTableEmptyState component (SVGs, dark mode, label/description)
- Reduces RuiDataTable imports from 21 to 19 (under max-dependencies limit)
- Use rowVariant for selected/empty/expandable/group row states
- Use ui.td({ class: getAlignClass(...) }) for body cell alignment
- Fix loading placeholder height: wrap in flex container with min-h-56
- Reuse getAlignClass from table-props.ts (re-exported via data-table-styles)
- Update e2e to check Tailwind classes instead of CSS module selectors
- Replace SCSS module with tv({ slots }) in button-styles.ts
- Extract ButtonVariant, ButtonSize, getButtonSpinnerSize to button-props.ts
- List variant self-contained in tv() with shared outlined/text color compounds
- Use compoundSlots for dark mode text overrides
- Split elevation into defaultElevation + usedElevation with named constants
- Use data-spinner attribute for loading state child visibility
- Add data-variant, data-color, data-active, data-id="btn-label" attributes
- Improve List story to showcase stacked menu-style usage
- Update ButtonGroup tests to use data-active attribute checks
- Delete composables/colors/button.ts (inlined into tv())
- Update e2e to use data-color and data-id selectors
…ned variant - Replace CSS modules with tv() (tailwind-variants) extending shared textInputBase - Move variant-specific classes (pt-3, py-1.5, border-b, underline pseudo) into variant definitions to avoid tv() extend class conflicts (tw-merge limitation) - Fix outlined variant: flush fieldset, proper label float with -translate-y-1/2, overflow-visible on inputWrapper, legend text content instead of broken ::after - Fix focused+error: override color with validation state so fieldset border shows error/success color even when focused - Fix clear button: instant show on focus, 500ms delay on blur via useTimeoutManager - Add tv() slots for all template elements: inputWrapper, details, required, clearButton — zero static class attributes in template - Add data-id attributes (wrapper, prepend, append, clear-btn) for robust testing - Update test selectors from CSS module classes to data-id and semantic selectors - Remove unused RuiTextInputOutlined component and useLabelWithQuote import - Slim down textInputBase.label (remove block/truncate not universal across components) - Legend padding (px-2) now compound-gated on showLabel+active (no notch when idle)
…le block - Create text-area-styles.ts extending shared textInputBase - Extract underlinePseudo and TextInputVariant to text-input-styles.ts for reuse - Rewrite template: zero static classes, all through tv() slots - Fix outlined variant: flush fieldset, label float with -translate-y-1/2 - Add data-id attributes (wrapper, prepend, append, clear-btn) - Add defineExpose for focus() and element (parity with TextField) - Performance: move CSS variables to wrapper (eliminate per-element computeds), debounce autoGrow watcher (50ms), remove redundant minHeight DOM write - Fix clear button: instant show on focus, 500ms delay on blur via useTimeoutManager - Update test selectors from CSS modules to data-id and semantic selectors
…riant, fix dropdown UX Migrate MenuSelect from CSS modules to tv() with shared activatorStyles. Add filled variant with Material Design spec (grey bg, underline, rounded top). Fix default variant to use transparent bg with underline per spec. Simplify dropdown-menu composable: replace DOM-query-based scroll with virtual list's scrollTo(), remove autoFocus (was stealing keyboard focus), remove @mousedown handler that caused virtual scroll shift on click. Add keyboard support: Enter, Space, Home, End keys on activator. Wire up applyHighlighted from composable instead of local duplicate.
Migrate AutoComplete from CSS modules to tv() with shared activatorStyles. Remove @mousedown handler on menu items (same virtual scroll shift fix as MenuSelect). Add keyboard Home/End support. Fix OutlinedDense story args. Consolidate menu/highlighted/active slots into activatorStyles base so both menuSelectStyles and autoCompleteStyles inherit them without duplication. AutoComplete only overrides the value slot for flex-wrap chip layout. Update unit tests and e2e tests to use data-highlighted attribute and data-id selectors instead of CSS module class patterns.
…lock Migrate DateTimePicker from CSS modules to tv() with shared activatorStyles. Add iconPrepend slot for the calendar icon positioning. Implement filled variant (was declared in props but never styled). Simplify combinedErrorMessages computed. Replace useLabelWithQuote with legendText computed + useFormTextDetail. Add missing stories: Filled, Dense, Disabled, Readonly, WithErrorMessage, WithSuccessMessage, WithHint, HideDetails.
Migrate TimePicker from CSS modules to tv() with standalone timePickerStyles (clock face, digits, hand, numbers). Update e2e tests to use aria-label selectors instead of CSS module class patterns for digit elements.
All component style blocks have been migrated to inline Tailwind with tv(). The remaining SCSS files (style.scss, colors.scss, preview.scss) were already pure CSS with one unused SCSS variable ($context-colors). Convert to .css, update imports, remove sass from devDependencies.
…ple app Replace all $style CSS module references with inline Tailwind classes including dark: variants. No more <style> blocks in the example app.
Adjust padding, margins, gaps, and sizing across components to match M3 specifications while preserving custom size variants and shapes. - buttons: height md 2.25→2.5rem, h-padding 1→1.5rem, line-height 1.25rem - text fields: h-padding px-3→px-4, filled label x-padding 0.75→1rem - text area: same padding adjustments as text fields - chips: height 2.26→2rem, tile shape rounded-sm→rounded-lg, label/root padding - tabs: container height 2.625→3rem - tooltip: vertical padding py-1→py-2 - badge: sizes shifted down (sm 20→16, md 24→20, lg 28→24px), dot overlap fix - progress: circular default diameter 32→40px - menu: add py-2 vertical padding to content container
37-62% faster due to optimized object creation and reduced function overhead. Override peer dep to allow tailwind-merge v2 (v3 requires TW4).
Query the DOM directly via querySelector instead of caching chip elements in a Map. The cache held stale DOM refs between value changes and added complexity for minimal gain on small element sets.
Reduce MAX_CACHE_SIZE from 500 to 100 and replace full cache clear on overflow with single-entry LRU eviction using Map insertion order. Prevents cache thrashing while keeping memory bounded.
Break down onEnter into focused helpers: highlightedMatchesSearch, applyCustomValue, and submitClosestForm. Use early returns to flatten control flow and reduce cyclomatic complexity.
Replace pre-allocated arrays with length manipulation with simple push() pattern. V8 handles dynamic array growth efficiently, making the pre-allocation unnecessary complexity.
Lower the blur-to-close debounce timing for snappier UX when focus leaves the autocomplete. MaxWait reduced from 400ms to 200ms.
- search.ts: remove unsafe non-null assertion on LRU eviction, inline default filter to avoid per-evaluation allocation, wrap internalSearch return with readonly(), add eslint-disable for intentional Ref params - value.ts: merge duplicate filterValuesByOptions/filterValuesByIdentifier into single filterValues with getKey parameter - keyboard-navigation.ts: extract handleOpenMenuEnter to reduce onEnter complexity, wrap focusedValueIndex return with readonly()
Replace withDefaults() with destructured prop defaults and pass props explicitly to RuiTextField instead of v-bind spreading $props.
Use @floating-ui/dom directly with imperative computePosition calls, avoiding the reactive loop issues of @floating-ui/vue. Position and arrow styles applied via Object.assign instead of reactive bindings. - New composables/floating.ts with useFloating(), own Placement/Strategy const enums, library-agnostic naming (popover, visible, onLeaveComplete) - composables/popper.ts reduced to deprecated shim (PopperOptions + toFloatingOptions converter) - Tooltip/Menu: new `options` prop (FloatingOptions), deprecated `popper` prop preserved for backwards compat - Internal components migrated from :popper to :options - Stories updated to use DEFAULT_FLOATING_OPTIONS - Default offset changed from 8 to 2 - Arrow element positioned absolutely with inline styles
f1d3502 to
ca1ef7d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the infrastructure and first 3 batches of the SCSS → inline Tailwind migration (#465–#469, part of #473):
variant()utility (lightweight CVA),cx(), color composables (src/composables/colors/), surface tokens in themeclassNamesprop on 12 components with co-located types per componentuseTabScroll/useTabRoutingcomposable extraction anduseLinkreplacing<RouterLink custom>currentPlacementexposureKey patterns established
variant()object pattern with compoundVariants for complex state combinations.tsfiles for frequently-instantiated componentsdata-id,data-state,data-active,data-variantattributes replacing CSS module class selectorsas constobject + type pattern for enum-like valuesOther changes
Test plan
pnpm test:run)pnpm typecheck)pnpm lint)pnpm test:e2e)