fix: enable enforce-consistent-class-order tailwind lint rule (#9428)

## Summary

Enable `better-tailwindcss/enforce-consistent-class-order` lint rule and
auto-fix all 1027 violations across 263 files. Stacked on #9427.

## Changes

- **What**: Sort Tailwind classes into consistent order via `eslint
--fix`
- Enable `enforce-consistent-class-order` as `'error'` in eslint config
- Purely cosmetic reordering — no behavioral or visual changes

## Review Focus

Mechanical auto-fix PR — all changes are class reordering only. This is
the largest diff but lowest risk since it changes no class names, only
their order.

**Stack:** #9417#9427 → **this PR**

Fixes #9300 (partial — 3 of 3 rules)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9428-fix-enable-enforce-consistent-class-order-tailwind-lint-rule-31a6d73d3650811c9065f5178ba3e724)
by [Unito](https://www.unito.io)
This commit is contained in:
Christian Byrne
2026-03-05 17:24:34 -08:00
committed by GitHub
parent 60267fc64c
commit ef4e4a69d5
278 changed files with 1027 additions and 1027 deletions

View File

@@ -3,7 +3,7 @@
role="status"
tabindex="0"
:aria-label="t('sideToolbar.activeJobStatus', { status: statusText })"
class="flex flex-col gap-2 p-2 rounded-lg"
class="flex flex-col gap-2 rounded-lg p-2"
@mouseenter="hovered = true"
@mouseleave="hovered = false"
@focusin="hovered = true"
@@ -47,13 +47,13 @@
</div>
<!-- Footer: Progress bar or status text -->
<div class="flex gap-1.5 items-center h-5">
<div class="flex h-5 items-center gap-1.5">
<!-- Running state: percentage + progress bar -->
<template v-if="isRunning && hasProgressPercent(progressPercent)">
<span class="shrink-0 text-sm text-muted-foreground">
{{ Math.round(progressPercent ?? 0) }}%
</span>
<div class="flex-1 relative h-1 rounded-sm bg-secondary-background">
<div class="relative h-1 flex-1 rounded-sm bg-secondary-background">
<div
:class="progressBarPrimaryClass"
:style="progressPercentStyle(progressPercent)"

View File

@@ -1,13 +1,13 @@
<template>
<div
class="absolute left-2 bottom-2 flex flex-wrap justify-start gap-1 select-none"
class="absolute bottom-2 left-2 flex flex-wrap justify-start gap-1 select-none"
>
<span
v-for="badge in badges"
:key="badge.label"
:class="
cn(
'px-2 py-1 rounded-sm text-xs font-bold uppercase tracking-wider text-modal-card-tag-foreground bg-modal-card-tag-background break-all'
'rounded-sm bg-modal-card-tag-background px-2 py-1 text-xs font-bold tracking-wider break-all text-modal-card-tag-foreground uppercase'
)
"
>

View File

@@ -9,7 +9,7 @@
>
<template v-if="shouldShowLeftPanel" #leftPanelHeaderTitle>
<i class="icon-[comfy--ai-model] size-4" />
<h2 class="flex-auto select-none text-base font-semibold text-nowrap">
<h2 class="flex-auto text-base font-semibold text-nowrap select-none">
{{ displayTitle }}
</h2>
</template>
@@ -75,7 +75,7 @@
<ModelInfoPanel v-if="focusedAsset" :asset="focusedAsset" :cache-key />
<div
v-else
class="flex h-full items-center justify-center wrap-break-word p-6 text-center text-muted"
class="flex h-full items-center justify-center p-6 text-center wrap-break-word text-muted"
>
{{ $t('assetBrowser.modelInfo.selectModelPrompt') }}
</div>

View File

@@ -7,9 +7,9 @@
:tabindex="interactive ? 0 : -1"
:class="
cn(
'select-none rounded-2xl overflow-hidden transition-all duration-200 bg-modal-card-background p-2 gap-2 flex flex-col h-full',
'flex h-full flex-col gap-2 overflow-hidden rounded-2xl bg-modal-card-background p-2 transition-all duration-200 select-none',
interactive &&
'group appearance-none bg-transparent m-0 outline-none text-left hover:bg-secondary-background focus:bg-secondary-background border-none focus:outline-solid outline-base-foreground outline-4',
'group m-0 appearance-none border-none bg-transparent text-left outline-4 outline-base-foreground outline-none hover:bg-secondary-background focus:bg-secondary-background focus:outline-solid',
focused && 'bg-secondary-background outline-solid'
)
"
@@ -26,14 +26,14 @@
v-else
:src="asset.preview_url"
:alt="displayName"
class="size-full object-cover cursor-pointer"
class="size-full cursor-pointer object-cover"
/>
<AssetBadgeGroup :badges="asset.badges" />
<IconGroup
:class="
cn(
'absolute top-2 right-2 invisible group-hover:visible',
'invisible absolute top-2 right-2 group-hover:visible',
dropdownMenuButton?.isOpen && 'visible'
)
"
@@ -66,13 +66,13 @@
</MoreButton>
</IconGroup>
</div>
<div class="max-h-32 flex flex-col gap-2 justify-between flex-auto">
<div class="flex max-h-32 flex-auto flex-col justify-between gap-2">
<h3
:id="titleId"
v-tooltip.top="{ value: displayName, showDelay: tooltipDelay }"
:class="
cn(
'm-0 text-sm font-semibold line-clamp-2 wrap-anywhere',
'm-0 line-clamp-2 text-sm font-semibold wrap-anywhere',
'text-base-foreground'
)
"
@@ -84,13 +84,13 @@
v-tooltip.top="{ value: asset.secondaryText, showDelay: tooltipDelay }"
:class="
cn(
'm-0 text-sm line-clamp-2 [-webkit-box-orient:vertical] [-webkit-line-clamp:2] [display:-webkit-box] text-muted-foreground'
'm-0 line-clamp-2 [display:-webkit-box] text-sm text-muted-foreground [-webkit-box-orient:vertical] [-webkit-line-clamp:2]'
)
"
>
{{ asset.secondaryText }}
</p>
<div class="flex items-center justify-between gap-2 mt-auto">
<div class="mt-auto flex items-center justify-between gap-2">
<div class="flex gap-3 text-xs text-muted-foreground">
<span v-if="asset.stats.stars" class="flex items-center gap-1">
<i class="icon-[lucide--star] size-3" />
@@ -115,7 +115,7 @@
v-if="interactive"
variant="secondary"
size="lg"
class="shrink-0 relative"
class="relative shrink-0"
@click.stop="handleSelect"
>
{{ $t('g.use') }}
@@ -210,7 +210,7 @@ function confirmDeletion() {
confirmText: t('g.delete'),
// TODO: These need to be put into the new Button Variants once we have them.
confirmClass: cn(
'bg-danger-200 text-base-foreground hover:bg-danger-200/80 focus:bg-danger-200/80 focus:ring ring-base-foreground'
'bg-danger-200 text-base-foreground ring-base-foreground hover:bg-danger-200/80 focus:bg-danger-200/80 focus:ring'
),
optionsDisabled,
onCancel: () => {

View File

@@ -119,7 +119,7 @@ function closeDialog() {
v-else-if="job.status === 'completed' && job.downloadError"
>
<span
class="text-xs text-destructive-background truncate max-w-32"
class="max-w-32 truncate text-xs text-destructive-background"
>
{{ job.downloadError }}
</span>
@@ -178,7 +178,7 @@ function closeDialog() {
<template #footer="{ toggle }">
<div
class="flex flex-1 min-w-0 h-12 items-center justify-between gap-2 border-t border-border-default px-4"
class="flex h-12 min-w-0 flex-1 items-center justify-between gap-2 border-t border-border-default px-4"
>
<div class="flex min-w-0 flex-1 items-center gap-2 text-sm">
<i
@@ -188,7 +188,7 @@ function closeDialog() {
<span
:class="
cn(
'truncate font-bold text-base-foreground transition-all duration-300 overflow-hidden',
'truncate overflow-hidden font-bold text-base-foreground transition-all duration-300',
isExpanded ? 'min-w-0 flex-1' : 'w-0'
)
"
@@ -202,7 +202,7 @@ function closeDialog() {
v-if="isInProgress"
:class="
cn(
'text-sm text-muted-foreground transition-all duration-300 overflow-hidden',
'overflow-hidden text-sm text-muted-foreground transition-all duration-300',
isExpanded ? 'whitespace-nowrap' : 'w-0'
)
"

View File

@@ -1,10 +1,10 @@
<template>
<div
class="flex gap-4 items-center justify-between px-6 pt-2 pb-6"
class="flex items-center justify-between gap-4 px-6 pt-2 pb-6"
data-component-id="asset-filter-bar"
>
<div
class="flex gap-4 items-center"
class="flex items-center gap-4"
data-component-id="asset-filter-bar-left"
>
<MultiSelect

View File

@@ -15,13 +15,13 @@
</div>
<div
v-else-if="assets.length === 0"
class="flex h-full select-none flex-col items-center justify-center py-16 text-muted-foreground"
class="flex h-full flex-col items-center justify-center py-16 text-muted-foreground select-none"
>
<i class="mb-4 icon-[lucide--search] size-10" />
<h3 class="mb-2 text-lg font-medium">
{{ emptyTitle ?? $t('assetBrowser.noAssetsFound') }}
</h3>
<p class="text-sm whitespace-pre-wrap text-center">
<p class="text-center text-sm whitespace-pre-wrap">
{{ emptyMessage ?? $t('assetBrowser.tryAdjustingFilters') }}
</p>
</div>

View File

@@ -13,10 +13,10 @@
:tabindex="loading ? -1 : 0"
:class="
cn(
'flex flex-col overflow-hidden cursor-pointer p-2 transition-colors duration-200 rounded-lg',
'gap-2 select-none group',
'flex cursor-pointer flex-col overflow-hidden rounded-lg p-2 transition-colors duration-200',
'group gap-2 select-none',
selected
? 'ring-3 ring-inset ring-modal-card-border-highlighted'
? 'ring-3 ring-modal-card-border-highlighted ring-inset'
: 'hover:bg-modal-card-background-hovered/20'
)
"
@@ -84,7 +84,7 @@
<!-- Bottom Area: Media Info -->
<div class="flex-1">
<!-- Loading State -->
<div v-if="loading" class="flex justify-between items-start">
<div v-if="loading" class="flex items-start justify-between">
<div class="flex flex-col gap-1">
<div
class="h-4 w-24 animate-pulse rounded-sm bg-modal-card-background"
@@ -101,7 +101,7 @@
<!-- Content -->
<div
v-else-if="asset && adaptedAsset"
class="flex justify-between items-end gap-1.5"
class="flex items-end justify-between gap-1.5"
>
<!-- Left side: Media name and metadata -->
<div class="flex flex-col gap-1">

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex gap-3 items-center">
<div class="flex items-center gap-3">
<SearchBox
:model-value="searchQuery"
:placeholder="
@@ -7,7 +7,7 @@
"
@update:model-value="handleSearchChange"
/>
<div class="flex gap-1.5 items-center">
<div class="flex items-center gap-1.5">
<MediaAssetFilterButton
v-if="isCloud"
v-tooltip.top="{ value: $t('assetBrowser.filterBy') }"

View File

@@ -11,7 +11,7 @@ TODO: Extract checkbox pattern into reusable Checkbox component
- Benefits: Consistent checkbox UI, better maintainability, reusable design system component
-->
<template>
<div class="flex flex-col gap-0 p-0 m-0">
<div class="m-0 flex flex-col gap-0 p-0">
<div
v-for="filter in filters"
:key="filter.type"
@@ -27,7 +27,7 @@ TODO: Extract checkbox pattern into reusable Checkbox component
class="flex size-4 shrink-0 items-center justify-center rounded-sm p-0.5 transition-all duration-200"
:class="
mediaTypeFilters.includes(filter.type)
? 'bg-primary-background border-primary-background'
? 'border-primary-background bg-primary-background'
: 'bg-secondary-background'
"
>

View File

@@ -10,7 +10,7 @@
<span>{{ $t('sideToolbar.queueProgressOverlay.viewList') }}</span>
</span>
<i
class="icon-[lucide--check] ml-auto size-4"
class="ml-auto icon-[lucide--check] size-4"
:class="viewMode !== 'list' && 'opacity-0'"
/>
</Button>
@@ -25,13 +25,13 @@
<span>{{ $t('sideToolbar.queueProgressOverlay.viewGrid') }}</span>
</span>
<i
class="icon-[lucide--check] ml-auto size-4"
class="ml-auto icon-[lucide--check] size-4"
:class="viewMode !== 'grid' && 'opacity-0'"
/>
</Button>
<template v-if="showSortOptions">
<div class="border-b w-full border-border-subtle my-1" />
<div class="my-1 w-full border-b border-border-subtle" />
<Button
variant="textonly"
@@ -40,7 +40,7 @@
>
<span>{{ $t('sideToolbar.mediaAssets.sortNewestFirst') }}</span>
<i
class="icon-[lucide--check] ml-auto size-4"
class="ml-auto icon-[lucide--check] size-4"
:class="sortBy !== 'newest' && 'opacity-0'"
/>
</Button>
@@ -52,7 +52,7 @@
>
<span>{{ $t('sideToolbar.mediaAssets.sortOldestFirst') }}</span>
<i
class="icon-[lucide--check] ml-auto size-4"
class="ml-auto icon-[lucide--check] size-4"
:class="sortBy !== 'oldest' && 'opacity-0'"
/>
</Button>
@@ -65,7 +65,7 @@
>
<span>{{ $t('sideToolbar.mediaAssets.sortLongestFirst') }}</span>
<i
class="icon-[lucide--check] ml-auto size-4"
class="ml-auto icon-[lucide--check] size-4"
:class="sortBy !== 'longest' && 'opacity-0'"
/>
</Button>
@@ -77,7 +77,7 @@
>
<span>{{ $t('sideToolbar.mediaAssets.sortFastestFirst') }}</span>
<i
class="icon-[lucide--check] ml-auto size-4"
class="ml-auto icon-[lucide--check] size-4"
:class="sortBy !== 'fastest' && 'opacity-0'"
/>
</Button>

View File

@@ -1,6 +1,6 @@
<template>
<p
class="m-0 line-clamp-2 text-sm/tight text-base-foreground break-all"
class="m-0 line-clamp-2 text-sm/tight break-all text-base-foreground"
:title="fileName"
>
{{ fileName }}

View File

@@ -144,7 +144,7 @@ function closeDialog() {
<div class="relative max-h-75 overflow-y-auto p-4">
<div
v-if="filteredJobs.length > 3"
class="absolute right-1 top-4 h-12 w-1 rounded-full bg-muted-foreground"
class="absolute top-4 right-1 h-12 w-1 rounded-full bg-muted-foreground"
/>
<div class="flex flex-col gap-2">
@@ -208,7 +208,7 @@ function closeDialog() {
<div class="flex shrink-0 items-center gap-2">
<span
v-if="isInProgress"
class="whitespace-nowrap text-sm text-muted-foreground"
class="text-sm whitespace-nowrap text-muted-foreground"
>
{{
t('progressToast.progressCount', {

View File

@@ -2,7 +2,7 @@
<div class="flex items-center gap-2 p-4 font-bold">
<span>{{ $t('assetBrowser.uploadModelGeneric') }}</span>
<span
class="rounded-full bg-white px-1.5 py-0 text-xxs font-inter font-semibold uppercase text-black"
class="rounded-full bg-white px-1.5 py-0 font-inter text-xxs font-semibold text-black uppercase"
>
{{ $t('g.beta') }}
</span>

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex justify-end gap-2 w-full">
<div class="flex w-full justify-end gap-2">
<div v-if="currentStep === 1" class="mr-auto flex items-center gap-2">
<i class="icon-[lucide--circle-question-mark] text-muted-foreground" />
<Button

View File

@@ -72,7 +72,7 @@
<p class="m-0 text-sm font-bold">
{{ $t('assetBrowser.uploadFailed') }}
</p>
<p v-if="error" class="text-sm text-muted mb-0">
<p v-if="error" class="mb-0 text-sm text-muted">
{{ error }}
</p>
</div>

View File

@@ -1,6 +1,6 @@
<template>
<div
class="flex flex-col justify-between gap-10 p-4 border-t border-border-default w-auto max-w-[min(500px,90vw)]"
class="flex w-auto max-w-[min(500px,90vw)] flex-col justify-between gap-10 border-t border-border-default p-4"
>
<UploadModelUpgradeModalBody />

View File

@@ -1,10 +1,10 @@
<template>
<div class="flex flex-wrap justify-end gap-2 w-full">
<div class="flex w-full flex-wrap justify-end gap-2">
<a
href="https://blog.comfy.org/p/comfy-cloud-new-features-and-pricing"
target="_blank"
rel="noopener noreferrer"
class="text-muted-foreground mr-auto underline flex items-center gap-2"
class="mr-auto flex items-center gap-2 text-muted-foreground underline"
>
<i class="icon-[lucide--external-link]" />
<span>{{ $t('g.learnMore') }}</span>

View File

@@ -1,15 +1,15 @@
<template>
<div class="flex flex-col justify-between h-full gap-6 text-sm">
<div class="flex h-full flex-col justify-between gap-6 text-sm">
<div class="flex flex-col gap-6">
<div class="flex flex-col gap-2">
<p class="m-0 text-foreground">
<p class="text-foreground m-0">
{{ $t('assetBrowser.uploadModelDescription1Generic') }}
</p>
<div class="m-0">
<p class="m-0 text-muted-foreground">
{{ $t('assetBrowser.uploadModelDescription2Generic') }}
</p>
<span class="inline-flex items-center gap-1 flex-wrap mt-2">
<span class="mt-2 inline-flex flex-wrap items-center gap-1">
<span class="inline-flex items-center gap-1">
<img
:src="civitaiIcon"
@@ -65,7 +65,7 @@
<Button
variant="textonly"
size="unset"
class="text-muted-foreground underline p-0"
class="p-0 text-muted-foreground underline"
@click="openSecretsSettings"
>
{{ $t('assetBrowser.apiKeyHintLink') }}

View File

@@ -1,6 +1,6 @@
<template>
<div class="flex flex-col gap-2 px-4 py-2 text-sm text-base-foreground">
<div class="flex items-center justify-between relative">
<div class="relative flex items-center justify-between">
<span class="select-none">{{ label }}</span>
<slot name="label-action" />
</div>

View File

@@ -1,11 +1,11 @@
<template>
<div
data-component-id="ModelInfoPanel"
class="flex h-full flex-col scrollbar-custom"
class="flex scrollbar-custom h-full flex-col"
>
<PropertiesAccordionItem :class="accordionClass">
<template #label>
<span class="text-xs uppercase font-inter select-none">
<span class="font-inter text-xs uppercase select-none">
{{ t('assetBrowser.modelInfo.basicInfo') }}
</span>
</template>
@@ -14,7 +14,7 @@
<EditableText
:model-value="displayName"
:is-editing="isEditingDisplayName"
:class="cn('break-all text-muted-foreground flex-auto')"
:class="cn('flex-auto break-all text-muted-foreground')"
@dblclick="isEditingDisplayName = !isImmutable"
@edit="handleDisplayNameEdit"
@cancel="isEditingDisplayName = false"
@@ -23,11 +23,11 @@
v-if="!isImmutable && !isEditingDisplayName"
size="icon-sm"
variant="muted-textonly"
class="transition-opacity opacity-0 group-hover:opacity-100"
class="opacity-0 transition-opacity group-hover:opacity-100"
:aria-label="t('assetBrowser.modelInfo.editDisplayName')"
@click="isEditingDisplayName = !isImmutable"
>
<i class="icon-[lucide--square-pen] self-center size-4" />
<i class="icon-[lucide--square-pen] size-4 self-center" />
</Button>
</div>
</ModelInfoField>
@@ -42,7 +42,7 @@
:href="sourceUrl"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-1.5 text-muted-foreground no-underline transition-colors hover:text-foreground"
class="hover:text-foreground inline-flex items-center gap-1.5 text-muted-foreground no-underline transition-colors"
>
<img
v-if="sourceName === 'Civitai'"
@@ -64,7 +64,7 @@
<PropertiesAccordionItem :class="accordionClass">
<template #label>
<span class="text-xs uppercase font-inter select-none">
<span class="font-inter text-xs uppercase select-none">
{{ t('assetBrowser.modelInfo.modelTagging') }}
</span>
</template>
@@ -140,7 +140,7 @@
<PropertiesAccordionItem :class="accordionClass">
<template #label>
<span class="text-xs uppercase font-inter select-none">
<span class="font-inter text-xs uppercase select-none">
{{ t('assetBrowser.modelInfo.modelDescription') }}
</span>
</template>
@@ -157,7 +157,7 @@
class="p-0"
@click="copyToClipboard(triggerPhrases.join(', '))"
>
<i class="icon-[lucide--copy] size-4 min-w-4 min-h-4 opacity-60" />
<i class="icon-[lucide--copy] size-4 min-h-4 min-w-4 opacity-60" />
</Button>
</template>
<div class="flex flex-wrap gap-1 pt-1">
@@ -167,11 +167,11 @@
variant="muted-textonly"
size="unset"
:title="t('g.copyToClipboard')"
class="text-pretty whitespace-normal text-left text-xs"
class="text-left text-xs text-pretty whitespace-normal"
@click="copyToClipboard(phrase)"
>
{{ phrase }}
<i class="icon-[lucide--copy] size-4 min-w-4 min-h-4 opacity-60" />
<i class="icon-[lucide--copy] size-4 min-h-4 min-w-4 opacity-60" />
</Button>
</div>
</ModelInfoField>
@@ -194,7 +194,7 @@
rows="3"
:class="
cn(
'w-full resize-y rounded-lg border border-transparent bg-transparent px-3 py-2 text-sm text-component-node-foreground outline-none transition-colors focus:bg-component-node-widget-background',
'w-full resize-y rounded-lg border border-transparent bg-transparent px-3 py-2 text-sm text-component-node-foreground transition-colors outline-none focus:bg-component-node-widget-background',
isImmutable && 'cursor-not-allowed'
)
"
@@ -251,7 +251,7 @@ const descriptionTextarea = useTemplateRef<HTMLTextAreaElement>(
)
const accordionClass = cn(
'bg-modal-panel-background border-t border-border-default'
'border-t border-border-default bg-modal-panel-background'
)
const { asset, cacheKey } = defineProps<{