Skip to content

Conversation

@georgewrmarshall
Copy link
Contributor

@georgewrmarshall georgewrmarshall commented Jul 10, 2025

Description

This PR adds the ESLint Tailwind CSS plugin to enforce the Tailwind CSS best practices and some of the guidelines established in the MetaMask contributor docs. The plugin provides automated linting to ensure consistent usage of Tailwind CSS classes across the MetaMask Extension codebase.

Key changes:

  • Added eslint-plugin-tailwindcss and its TypeScript definitions
  • Configured ESLint rules to enforce Tailwind CSS best practices including:
    • Class name ordering for consistency
    • Shorthand enforcement (e.g., p-4 instead of px-4 py-4)
    • Prevention of contradicting class names
    • Elimination of unnecessary arbitrary values
  • Applied automatic fixes to existing code to comply with the new linting rules

This change supports the broader design system unification effort by ensuring consistent Tailwind CSS usage across all MetaMask applications, improving code maintainability and reducing styling inconsistencies.

Open in GitHub Codespaces

Changelog

CHANGELOG entry: null

Related issues

Fixes: #34210

Manual testing steps

  1. Pull the branch and install dependencies with yarn install
  2. Run ESLint on files with Tailwind classes: yarn lint
  3. Verify that Tailwind CSS class violations are flagged as errors
  4. Test that the automatic fixes maintain visual consistency by running Storybook: yarn storybook
  5. Navigate to Design System stories and verify the styling remains unchanged

Screenshots/Recordings

Before

  • ESLint would not catch Tailwind CSS type errors
before.eslint.mov

After

  • ESLint now enforces Tailwind CSS best practices automatically
  • Classes are optimized (e.g., px-4 py-4p-4, proper class ordering)
  • Consistent code style across the codebase
tailwind.eslint.720.mov

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

@georgewrmarshall georgewrmarshall self-assigned this Jul 10, 2025
@georgewrmarshall georgewrmarshall added the team-design-system All issues relating to design system in Extension label Jul 10, 2025
@georgewrmarshall georgewrmarshall requested a review from Copilot July 10, 2025 18:30
@github-actions
Copy link
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR integrates the eslint-plugin-tailwindcss into the codebase and applies automatic fixes to existing components to enforce Tailwind CSS best practices.

  • Installed the Tailwind CSS ESLint plugin and its TypeScript definitions.
  • Updated ESLint configuration to enable Tailwind rules (ordering, shorthand enforcement, no contradicting classes, etc.).
  • Refactored several className attributes in the design system stories to use shorthand classes (e.g., p-4, border-muted, reordering for consistency).

Reviewed Changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated no comments.

File Description
ui/pages/design-system/design-system.stories.tsx Converted multiple px-4 py-4 patterns to p-4, replaced custom border-color classes, and reordered classes for consistency.
package.json Added eslint-plugin-tailwindcss and @types/eslint-plugin-tailwindcss to dependencies.
.eslintrc.js Added tailwindcss to the ESLint plugins array, enabled its rules, and configured plugin settings.
Comments suppressed due to low confidence (1)

.eslintrc.js:366

  • [nitpick] Consider updating your contributor documentation (e.g., CONTRIBUTING.md or README) to mention the new Tailwind CSS ESLint plugin and its rules so that all contributors are aware of the enforced class ordering and shorthand conventions.
      plugins: ['react', 'tailwindcss'],

@socket-security
Copy link

socket-security bot commented Jul 10, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​eslint-plugin-tailwindcss@​3.17.0961006276100
Addedeslint-plugin-tailwindcss@​3.18.09910010090100

View full report

@georgewrmarshall georgewrmarshall force-pushed the tailwind-eslint-plugin branch from 402724b to 5acaba2 Compare July 14, 2025 17:18
@metamaskbot
Copy link
Collaborator

Builds ready [5acaba2]
UI Startup Metrics (1237 ± 66 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyHomeuiStartup1237111414716612811366
load105794812405411051159
domContentLoaded105194012375410961153
domInteractive18145171739
firstPaint62686117342510421119
backgroundConnect73213812
firstReactRender20145052134
getState17655102135
initialActions50739416
loadScripts79868397452834891
setupStore85314916
WebpackHomeuiStartup22421710277322423822660
load16811265204418217791971
domContentLoaded16741261202618017731956
domInteractive191376141563
firstPaint1555935863200277
backgroundConnect23105782639
firstReactRender25688445123382400
getState185331331743
initialActions82397928
loadScripts16711259201417817711943
setupStore187173172137
FirefoxBrowserifyHomeuiStartup13801180198814914271695
load1203104515709812601409
domContentLoaded1202104515699812601409
domInteractive1093549978108287
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect2113197192030
firstReactRender23195442429
getState13533834931
initialActions506411319
loadScripts1184103515529612421395
setupStore16422335668
WebpackHomeuiStartup17921477337926818812346
load15041248312923515801884
domContentLoaded15031247312823515801883
domInteractive1083447878100317
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect25176082741
firstReactRender59467766371
getState187208281535
initialActions10126137422
loadScripts14821226311123415621860
setupStore166267351037
Benchmark value 1238 exceeds gate value 1234 for chrome browserify home mean uiStartup
Benchmark value 17 exceeds gate value 15 for chrome browserify home mean getState
Benchmark value 5 exceeds gate value 1 for chrome browserify home mean initialActions
Benchmark value 1367 exceeds gate value 1365 for chrome browserify home p95 uiStartup
Benchmark value 35 exceeds gate value 33 for chrome browserify home p95 getState
Benchmark value 16 exceeds gate value 1.2 for chrome browserify home p95 initialActions
Benchmark value 2243 exceeds gate value 2192 for chrome webpack home mean uiStartup
Benchmark value 257 exceeds gate value 235 for chrome webpack home mean firstReactRender
Benchmark value 9 exceeds gate value 7 for chrome webpack home mean initialActions
Benchmark value 2661 exceeds gate value 2454 for chrome webpack home p95 uiStartup
Benchmark value 64 exceeds gate value 57 for chrome webpack home p95 domInteractive
Benchmark value 400 exceeds gate value 370 for chrome webpack home p95 firstReactRender
Benchmark value 28 exceeds gate value 7 for chrome webpack home p95 initialActions
Benchmark value 13 exceeds gate value 11 for firefox browserify home mean getState
Benchmark value 5 exceeds gate value 1 for firefox browserify home mean initialActions
Benchmark value 16 exceeds gate value 9 for firefox browserify home mean setupStore
Benchmark value 1695 exceeds gate value 1660 for firefox browserify home p95 uiStartup
Benchmark value 287 exceeds gate value 195 for firefox browserify home p95 domInteractive
Benchmark value 31 exceeds gate value 24 for firefox browserify home p95 getState
Benchmark value 19 exceeds gate value 2 for firefox browserify home p95 initialActions
Benchmark value 68 exceeds gate value 27 for firefox browserify home p95 setupStore
Benchmark value 1793 exceeds gate value 1615 for firefox webpack home mean uiStartup
Benchmark value 1504 exceeds gate value 1380 for firefox webpack home mean load
Benchmark value 1504 exceeds gate value 1380 for firefox webpack home mean domContentLoaded
Benchmark value 109 exceeds gate value 100 for firefox webpack home mean domInteractive
Benchmark value 60 exceeds gate value 38 for firefox webpack home mean firstReactRender
Benchmark value 18 exceeds gate value 15 for firefox webpack home mean getState
Benchmark value 10 exceeds gate value 1 for firefox webpack home mean initialActions
Benchmark value 1483 exceeds gate value 1360 for firefox webpack home mean loadScripts
Benchmark value 16 exceeds gate value 13 for firefox webpack home mean setupStore
Benchmark value 2346 exceeds gate value 1935 for firefox webpack home p95 uiStartup
Benchmark value 1884 exceeds gate value 1660 for firefox webpack home p95 load
Benchmark value 1883 exceeds gate value 1660 for firefox webpack home p95 domContentLoaded
Benchmark value 317 exceeds gate value 156 for firefox webpack home p95 domInteractive
Benchmark value 71 exceeds gate value 50 for firefox webpack home p95 firstReactRender
Benchmark value 35 exceeds gate value 32 for firefox webpack home p95 getState
Benchmark value 22 exceeds gate value 2 for firefox webpack home p95 initialActions
Benchmark value 1860 exceeds gate value 1630 for firefox webpack home p95 loadScripts
Benchmark value 37 exceeds gate value 28 for firefox webpack home p95 setupStore
Sum of mean exceeds: 693ms | Sum of p95 exceeds: 1777.8ms
Sum of all benchmark exceeds: 2470.8ms

Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 33 Bytes (0%)
  • ui: 0 Bytes (0%)
  • common: 93 Bytes (0%)

@metamaskbot
Copy link
Collaborator

Builds ready [1bc1b6d]
UI Startup Metrics (1234 ± 66 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyHomeuiStartup1234110614746612791336
load105792412516210981159
domContentLoaded105091712436110881153
domInteractive19148291739
firstPaint69382125442310691141
backgroundConnect2552392888260269
firstReactRender20147772134
getState943171127
initialActions20173111
loadScripts80067596659837904
setupStore74162712
WebpackHomeuiStartup22621689287621823682663
load17061284215516818061999
domContentLoaded17001279213716717991996
domInteractive181281141563
firstPaint159651290133164316
backgroundConnect2611232232948
firstReactRender24887411118375399
getState124124131329
initialActions9222423828
loadScripts16961275212616517951985
setupStore1464081634
FirefoxBrowserifyHomeuiStartup14521252223516814821792
load12581095182710713221405
domContentLoaded12581095182710713211405
domInteractive12436803106123339
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect2714223252667
firstReactRender24213732532
getState12218628664
initialActions5018821211
loadScripts12361077180910812971384
setupStore9419220636
WebpackHomeuiStartup17521425239420618362208
load14751220204917215661832
domContentLoaded14741219204917215661831
domInteractive108375237895288
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect271693132949
firstReactRender57438666166
getState10361101325
initialActions6017017421
loadScripts14531201201716915451807
setupStore14526033935
Benchmark value 1235 exceeds gate value 1234 for chrome browserify home mean uiStartup
Benchmark value 256 exceeds gate value 10 for chrome browserify home mean backgroundConnect
Benchmark value 3 exceeds gate value 1 for chrome browserify home mean initialActions
Benchmark value 270 exceeds gate value 18 for chrome browserify home p95 backgroundConnect
Benchmark value 11 exceeds gate value 1.2 for chrome browserify home p95 initialActions
Benchmark value 2262 exceeds gate value 2192 for chrome webpack home mean uiStartup
Benchmark value 249 exceeds gate value 235 for chrome webpack home mean firstReactRender
Benchmark value 10 exceeds gate value 7 for chrome webpack home mean initialActions
Benchmark value 2663 exceeds gate value 2454 for chrome webpack home p95 uiStartup
Benchmark value 64 exceeds gate value 57 for chrome webpack home p95 domInteractive
Benchmark value 399 exceeds gate value 370 for chrome webpack home p95 firstReactRender
Benchmark value 28 exceeds gate value 7 for chrome webpack home p95 initialActions
Benchmark value 1985 exceeds gate value 1970 for chrome webpack home p95 loadScripts
Benchmark value 1452 exceeds gate value 1405 for firefox browserify home mean uiStartup
Benchmark value 1259 exceeds gate value 1245 for firefox browserify home mean load
Benchmark value 1258 exceeds gate value 1239 for firefox browserify home mean domContentLoaded
Benchmark value 124 exceeds gate value 110 for firefox browserify home mean domInteractive
Benchmark value 28 exceeds gate value 25 for firefox browserify home mean backgroundConnect
Benchmark value 12 exceeds gate value 11 for firefox browserify home mean getState
Benchmark value 5 exceeds gate value 1 for firefox browserify home mean initialActions
Benchmark value 1236 exceeds gate value 1230 for firefox browserify home mean loadScripts
Benchmark value 10 exceeds gate value 9 for firefox browserify home mean setupStore
Benchmark value 1792 exceeds gate value 1660 for firefox browserify home p95 uiStartup
Benchmark value 339 exceeds gate value 195 for firefox browserify home p95 domInteractive
Benchmark value 64 exceeds gate value 24 for firefox browserify home p95 getState
Benchmark value 11 exceeds gate value 2 for firefox browserify home p95 initialActions
Benchmark value 36 exceeds gate value 27 for firefox browserify home p95 setupStore
Benchmark value 1752 exceeds gate value 1615 for firefox webpack home mean uiStartup
Benchmark value 1475 exceeds gate value 1380 for firefox webpack home mean load
Benchmark value 1475 exceeds gate value 1380 for firefox webpack home mean domContentLoaded
Benchmark value 108 exceeds gate value 100 for firefox webpack home mean domInteractive
Benchmark value 28 exceeds gate value 26 for firefox webpack home mean backgroundConnect
Benchmark value 58 exceeds gate value 38 for firefox webpack home mean firstReactRender
Benchmark value 6 exceeds gate value 1 for firefox webpack home mean initialActions
Benchmark value 1453 exceeds gate value 1360 for firefox webpack home mean loadScripts
Benchmark value 15 exceeds gate value 13 for firefox webpack home mean setupStore
Benchmark value 2208 exceeds gate value 1935 for firefox webpack home p95 uiStartup
Benchmark value 1832 exceeds gate value 1660 for firefox webpack home p95 load
Benchmark value 1831 exceeds gate value 1660 for firefox webpack home p95 domContentLoaded
Benchmark value 288 exceeds gate value 156 for firefox webpack home p95 domInteractive
Benchmark value 66 exceeds gate value 50 for firefox webpack home p95 firstReactRender
Benchmark value 21 exceeds gate value 2 for firefox webpack home p95 initialActions
Benchmark value 1807 exceeds gate value 1630 for firefox webpack home p95 loadScripts
Benchmark value 35 exceeds gate value 28 for firefox webpack home p95 setupStore
Sum of mean exceeds: 902ms | Sum of p95 exceeds: 1843.8ms
Sum of all benchmark exceeds: 2745.8ms

Bundle size diffs [🚀 Bundle size reduced!]
  • background: -33.23 KiB (-0.61%)
  • ui: 9.8 KiB (0.13%)
  • common: -41.06 KiB (-0.48%)

@metamaskbot
Copy link
Collaborator

Builds ready [b263d3e]
UI Startup Metrics (1284 ± 53 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyHomeuiStartup1284116214745313251364
load109898912825511341182
domContentLoaded109198212795611291176
domInteractive18145571738
firstPaint66297119244411051165
backgroundConnect2162022457219231
firstReactRender22156282439
getState1046481223
initialActions40275317
loadScripts881761105155920962
setupStore74273812
WebpackHomeuiStartup23971905298117824502758
load18851413240118119932195
domContentLoaded18751408230017319902162
domInteractive2112191271558
firstPaint1516342569173296
backgroundConnect57103228929287
firstReactRender1658737881193357
getState3133917115253
initialActions8220520715
loadScripts18681404228617119582159
setupStore176248321431
FirefoxBrowserifyHomeuiStartup15051341210614315521873
load1302115116039313611494
domContentLoaded1302115116029313601494
domInteractive1133850472112283
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect2915160193165
firstReactRender28216392855
getState112153191337
initialActions40416317
loadScripts1279113515839113371473
setupStore11321830628
WebpackHomeuiStartup18141537252519219142187
load15161316204414715841796
domContentLoaded15161316204314715841796
domInteractive100334087093341
firstPaintNaNNaNNaNNaNNaNNaN
backgroundConnect3418300363354
firstReactRender55457665968
getState143244251738
initialActions101228251126
loadScripts14901296201214315621770
setupStore165249341635
Benchmark value 1285 exceeds gate value 1234 for chrome browserify home mean uiStartup
Benchmark value 1098 exceeds gate value 1070 for chrome browserify home mean load
Benchmark value 1092 exceeds gate value 1061 for chrome browserify home mean domContentLoaded
Benchmark value 217 exceeds gate value 10 for chrome browserify home mean backgroundConnect
Benchmark value 4 exceeds gate value 1 for chrome browserify home mean initialActions
Benchmark value 882 exceeds gate value 830 for chrome browserify home mean loadScripts
Benchmark value 231 exceeds gate value 18 for chrome browserify home p95 backgroundConnect
Benchmark value 17 exceeds gate value 1.2 for chrome browserify home p95 initialActions
Benchmark value 963 exceeds gate value 940 for chrome browserify home p95 loadScripts
Benchmark value 2397 exceeds gate value 2192 for chrome webpack home mean uiStartup
Benchmark value 1885 exceeds gate value 1711 for chrome webpack home mean load
Benchmark value 1875 exceeds gate value 1704 for chrome webpack home mean domContentLoaded
Benchmark value 22 exceeds gate value 21 for chrome webpack home mean domInteractive
Benchmark value 57 exceeds gate value 40 for chrome webpack home mean backgroundConnect
Benchmark value 31 exceeds gate value 29 for chrome webpack home mean getState
Benchmark value 8 exceeds gate value 7 for chrome webpack home mean initialActions
Benchmark value 1868 exceeds gate value 1699 for chrome webpack home mean loadScripts
Benchmark value 2758 exceeds gate value 2454 for chrome webpack home p95 uiStartup
Benchmark value 2196 exceeds gate value 2030 for chrome webpack home p95 load
Benchmark value 2162 exceeds gate value 2005 for chrome webpack home p95 domContentLoaded
Benchmark value 59 exceeds gate value 57 for chrome webpack home p95 domInteractive
Benchmark value 287 exceeds gate value 90 for chrome webpack home p95 backgroundConnect
Benchmark value 253 exceeds gate value 195 for chrome webpack home p95 getState
Benchmark value 15 exceeds gate value 7 for chrome webpack home p95 initialActions
Benchmark value 2160 exceeds gate value 1970 for chrome webpack home p95 loadScripts
Benchmark value 1506 exceeds gate value 1405 for firefox browserify home mean uiStartup
Benchmark value 1302 exceeds gate value 1245 for firefox browserify home mean load
Benchmark value 1302 exceeds gate value 1239 for firefox browserify home mean domContentLoaded
Benchmark value 114 exceeds gate value 110 for firefox browserify home mean domInteractive
Benchmark value 30 exceeds gate value 25 for firefox browserify home mean backgroundConnect
Benchmark value 28 exceeds gate value 25 for firefox browserify home mean firstReactRender
Benchmark value 12 exceeds gate value 11 for firefox browserify home mean getState
Benchmark value 5 exceeds gate value 1 for firefox browserify home mean initialActions
Benchmark value 1279 exceeds gate value 1230 for firefox browserify home mean loadScripts
Benchmark value 12 exceeds gate value 9 for firefox browserify home mean setupStore
Benchmark value 1873 exceeds gate value 1660 for firefox browserify home p95 uiStartup
Benchmark value 283 exceeds gate value 195 for firefox browserify home p95 domInteractive
Benchmark value 37 exceeds gate value 24 for firefox browserify home p95 getState
Benchmark value 17 exceeds gate value 2 for firefox browserify home p95 initialActions
Benchmark value 28 exceeds gate value 27 for firefox browserify home p95 setupStore
Benchmark value 1814 exceeds gate value 1615 for firefox webpack home mean uiStartup
Benchmark value 1517 exceeds gate value 1380 for firefox webpack home mean load
Benchmark value 1516 exceeds gate value 1380 for firefox webpack home mean domContentLoaded
Benchmark value 101 exceeds gate value 100 for firefox webpack home mean domInteractive
Benchmark value 34 exceeds gate value 26 for firefox webpack home mean backgroundConnect
Benchmark value 55 exceeds gate value 38 for firefox webpack home mean firstReactRender
Benchmark value 11 exceeds gate value 1 for firefox webpack home mean initialActions
Benchmark value 1491 exceeds gate value 1360 for firefox webpack home mean loadScripts
Benchmark value 16 exceeds gate value 13 for firefox webpack home mean setupStore
Benchmark value 2187 exceeds gate value 1935 for firefox webpack home p95 uiStartup
Benchmark value 1796 exceeds gate value 1660 for firefox webpack home p95 load
Benchmark value 1796 exceeds gate value 1660 for firefox webpack home p95 domContentLoaded
Benchmark value 341 exceeds gate value 156 for firefox webpack home p95 domInteractive
Benchmark value 54 exceeds gate value 49 for firefox webpack home p95 backgroundConnect
Benchmark value 68 exceeds gate value 50 for firefox webpack home p95 firstReactRender
Benchmark value 38 exceeds gate value 32 for firefox webpack home p95 getState
Benchmark value 26 exceeds gate value 2 for firefox webpack home p95 initialActions
Benchmark value 1770 exceeds gate value 1630 for firefox webpack home p95 loadScripts
Benchmark value 35 exceeds gate value 28 for firefox webpack home p95 setupStore
Sum of mean exceeds: 2044ms | Sum of p95 exceeds: 2572.8ms
Sum of all benchmark exceeds: 4616.8ms

Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: -22.76 KiB (-0.42%)
  • ui: 30.31 KiB (0.41%)
  • common: 27.25 KiB (0.32%)

{/* Container Expanded View */}
<Box className="mx-auto w-full bg-default md:max-w-xl md:rounded-3xl md:py-4">
{/* Header */}
<Box className="border-color-border-muted px-4 py-4 md:px-8">
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Lint fixes

@georgewrmarshall georgewrmarshall marked this pull request as ready for review July 22, 2025 02:01
@georgewrmarshall georgewrmarshall requested a review from a team July 22, 2025 02:01
@georgewrmarshall georgewrmarshall added this pull request to the merge queue Jul 22, 2025
Merged via the queue into main with commit 02bcf05 Jul 22, 2025
273 of 276 checks passed
@georgewrmarshall georgewrmarshall deleted the tailwind-eslint-plugin branch July 22, 2025 21:13
@github-actions github-actions bot locked and limited conversation to collaborators Jul 22, 2025
@metamaskbot metamaskbot added the release-13.1.0 Issue or pull request that will be included in release 13.1.0 label Jul 22, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-13.1.0 Issue or pull request that will be included in release 13.1.0 team-design-system All issues relating to design system in Extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add eslint tailwind plugin

5 participants