diff --git a/package.json b/package.json
index f5839d44e66814f2a742578cd4943d4cddfb55a6..8eb598f8feb78cc9ff7772838bf197b50e747dce 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,6 @@
     "lint:stylelint:fix": "yarn run lint:stylelint --fix",
     "lint:stylelint:staged": "scripts/frontend/execute-on-staged-files.sh stylelint '(css|scss)' -q",
     "lint:stylelint:staged:fix": "yarn run lint:stylelint:staged --fix",
-    "lint:tailwind-utils": "REDIRECT_TO_STDOUT=true node scripts/frontend/tailwind_lint_against_legacy_utils.js",
     "markdownlint": "markdownlint-cli2",
     "preinstall": "node ./scripts/frontend/preinstall.mjs",
     "postinstall": "node ./scripts/frontend/postinstall.js",
@@ -76,7 +75,7 @@
     "@gitlab/fonts": "^1.3.0",
     "@gitlab/query-language": "^0.0.5-a-20241017",
     "@gitlab/svgs": "3.119.0",
-    "@gitlab/ui": "98.5.2",
+    "@gitlab/ui": "99.0.0",
     "@gitlab/web-ide": "^0.0.1-dev-20240909013227",
     "@mattiasbuelens/web-streams-adapter": "^0.1.0",
     "@rails/actioncable": "7.0.8-4",
diff --git a/scripts/allowed_warnings.txt b/scripts/allowed_warnings.txt
index b8f3483159505cc43dfe4fd2e31d762ca09d2ce3..32f0ac8e7a0f7a3d2735934dc67813cd2a753eb7 100644
--- a/scripts/allowed_warnings.txt
+++ b/scripts/allowed_warnings.txt
@@ -31,11 +31,5 @@ warning: IO::Buffer is experimental and both the Ruby and C interface may change
 Rebuilding...
 Done in [0-9]+ms.
 
-# We are in the midst of legacy util to Tailwind migration and for linting purposes we are checking
-# whether someone uses legacy utils. If no legacy utils are found, tailwind sends a warning
-# This can be removed once we do not compile legacy utils any longer
-.+ - No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.
-.+ - https://tailwindcss.com/docs/content-configuration
-
 # The next major version of the view_component gem will require Rails >= 7.1
 Support for Rails versions < 7.1 is deprecated and will be removed from ViewComponent 4.0.0
diff --git a/scripts/frontend/tailwind_lint_against_legacy_utils.js b/scripts/frontend/tailwind_lint_against_legacy_utils.js
deleted file mode 100755
index 99f903896bbf03a5a7a0994ae1d8372ba75896ba..0000000000000000000000000000000000000000
--- a/scripts/frontend/tailwind_lint_against_legacy_utils.js
+++ /dev/null
@@ -1,220 +0,0 @@
-#!/usr/bin/env node
-
-/* eslint-disable import/extensions */
-
-const { readFile } = require('node:fs/promises');
-const path = require('node:path');
-const _ = require('lodash');
-const postcss = require('postcss');
-const tailwindPlugin = require('tailwindcss/plugin.js');
-const tailwindcss = require('tailwindcss/lib/plugin.js');
-const tailwindConfig = require('../../config/tailwind.config.js');
-
-const ROOT_PATH = path.resolve(__dirname, '../../');
-const tailwindSource = path.join(ROOT_PATH, 'app/assets/stylesheets/tailwind.css');
-const legacyUtilsSource = path.join(ROOT_PATH, 'node_modules/@gitlab/ui/dist/utility_classes.css');
-
-/**
- * Strips trailing modifiers like `:hover`, `::before` from selectors,
- * only returning the class name
- *
- * For example: .gl-foo-hover-bar:hover will be turned into: .gl-foo-bar
- * @param {String} selector
- * @returns {String}
- */
-function getCleanSelector(selector) {
-  return selector.replace(/:.*/, '');
-}
-
-/**
- * Extracts all class names from a CSS file
- *
- * @param {String} css
- * @returns {Set<String>}
- */
-function extractClassNames(css) {
-  const definitions = new Set();
-
-  postcss.parse(css).walkRules((rule) => {
-    // We skip all atrule, e.g. @keyframe, except @media queries
-    if (rule.parent?.type === 'atrule' && rule.parent?.name !== 'media') {
-      console.log(`Skipping atrule of type ${rule.parent?.name}`);
-      return;
-    }
-
-    // This is an odd dark-mode only util. We have added it to the dark mode overrides
-    // and remove it from our utility classes
-    if (rule.selector.startsWith('.gl-dark .gl-dark-invert-keep-hue')) {
-      console.log(`Skipping composite selector ${rule.selector} which will be migrated manually`);
-      return;
-    }
-
-    // iterate over each class definition
-    rule.selectors.forEach((selector) => {
-      definitions.add(getCleanSelector(selector));
-    });
-  });
-
-  return definitions;
-}
-
-/**
- * Writes the CSS in Js in compatibility mode. We write all the utils and we surface things we might
- * want to look into (hardcoded colors, definition mismatches).
- *
- * @param {Set<String>} tailwindClassNames
- * @param {Set<String>} oldClassNames
- */
-async function compareLegacyClassesWithTailwind(tailwindClassNames, oldClassNames) {
-  const oldUtilityNames = new Set(oldClassNames);
-
-  const deleted = new Set();
-
-  for (const definition of tailwindClassNames) {
-    if (oldUtilityNames.has(definition)) {
-      oldUtilityNames.delete(definition);
-      deleted.add(definition);
-    }
-  }
-
-  console.log(
-    `Legacy classes which have a tailwind equivalent:\n\t${_.chunk(Array.from(deleted), 4)
-      .map((n) => n.join(' '))
-      .join('\n\t')}`,
-  );
-
-  return { oldUtilityNames };
-}
-
-/**
- * Runs tailwind on the whole code base, but with mock utilities only.
- *
- * We hand in a Set of class names (e.g. `.foo-bar`, `.bar-baz`) and tailwind will run
- * if one of our source files contains e.g. `.gl-foo-bar` or `.gl-bar-baz`,
- * it will be returned
- *
- * @param {Set<String>} oldClassNames
- * @param {Array<string>} content
- * @returns {Promise<{rules: Set<String>}>}
- */
-async function toMinimalUtilities(oldClassNames, content = []) {
-  const { css: tailwindClasses } = await postcss([
-    tailwindcss({
-      ...tailwindConfig,
-      content: Array.isArray(content) && content.length > 0 ? content : tailwindConfig.content,
-      // We must ensure the GitLab UI plugin is disabled during this run so that whatever it defines
-      // is purged out of the CSS-in-Js.
-      presets: [
-        {
-          ...tailwindConfig.presets[0],
-          plugins: [],
-        },
-      ],
-      // Disable all core plugins, all we care about are the legacy utils
-      // that are provided via addUtilities.
-      corePlugins: [],
-      plugins: [
-        tailwindPlugin(({ addUtilities }) => {
-          addUtilities(
-            Object.fromEntries(
-              Array.from(oldClassNames).map((className) => [
-                // Strip leading `.gl-` because tailwind will add the prefix itself
-                className.replace(/^\.gl-/, '.'),
-                { width: 0 },
-              ]),
-            ),
-          );
-        }),
-      ],
-    }),
-  ]).process('@tailwind utilities;', { map: false, from: undefined });
-
-  const rules = tailwindClasses
-    .replace(/@.+?{([\s\S]+?)}/gim, '$1')
-    .replace(/\{[\s\S]+?}/gim, '')
-    .split('\n')
-    .map((x) => x.trim())
-    .filter(Boolean);
-
-  return { rules: new Set(rules) };
-}
-
-async function lintAgainstLegacyUtils({ content = [] } = {}) {
-  console.log('# Checking whether legacy GitLab utility classes are used');
-
-  console.log('## Extracting legacy util class names');
-
-  const legacyClassNames = extractClassNames(await readFile(legacyUtilsSource, 'utf-8'));
-
-  /**
-   * Document containing all utilities at least once, like this:
-   *
-   * <div class="gl-display-flex">
-   * <div class="gl-foo-bar">
-   * @type {string}
-   */
-  const allLegacyDocument = Array.from(legacyClassNames)
-    .map((className) => {
-      const cleanClass = className
-        .substring(1)
-        // replace escaped `\!` with !
-        .replace(/\\!/g, '!');
-
-      return `<div class="${cleanClass}"></div>`;
-    })
-    .join('\n');
-
-  const { css } = await postcss([
-    tailwindcss({
-      ...tailwindConfig,
-      content: [{ raw: allLegacyDocument, extension: 'html' }],
-      // We are disabling all plugins to prevent the CSS-in-Js import from causing trouble.
-      // The GitLab UI preset still registers its own plugin, which we need to define legitimate
-      // custom utils.
-      plugins: [],
-    }),
-  ]).process(await readFile(tailwindSource, 'utf-8'), { map: false, from: undefined });
-
-  const tailwindClassNames = extractClassNames(css);
-
-  console.log('## Comparing legacy utils to current tailwind class names');
-
-  const { oldUtilityNames } = await compareLegacyClassesWithTailwind(
-    tailwindClassNames,
-    legacyClassNames,
-  );
-
-  console.log('## Checking whether a legacy class name is used');
-
-  const { rules } = await toMinimalUtilities(oldUtilityNames, content);
-
-  console.log(`Went from ${oldUtilityNames.size} => ${rules.size} utility classes`);
-
-  if (rules.size > 0) {
-    const message = `You are introducing legacy utilities:
-\t${Array.from(rules).sort().join('\n\t')}
-Please migrate them to tailwind utilities:
-https://gitlab.com/gitlab-org/gitlab-ui/-/blob/main/doc/tailwind-migration.md`;
-    throw new Error(message);
-  }
-}
-
-function wasScriptCalledDirectly() {
-  return process.argv[1] === __filename;
-}
-
-if (wasScriptCalledDirectly()) {
-  lintAgainstLegacyUtils()
-    .then(() => {
-      console.log('# All good – Happiness. May the tailwind boost your journey');
-    })
-    .catch((e) => {
-      console.warn('An error happened');
-      console.warn(e.message);
-      process.exitCode = 1;
-    });
-}
-
-module.exports = {
-  lintAgainstLegacyUtils,
-};
diff --git a/scripts/static-analysis b/scripts/static-analysis
index 57d19dfdc797834b467fc1a66e38a3a8a3fa9b6a..fdfd988d839a986308826e211db728afc64bb021 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -43,7 +43,6 @@ class StaticAnalysis
     Task.new(%W[scripts/license-check.sh #{project_path}], 200),
     (Gitlab.ee? ? Task.new(%w[bin/rake gettext:updated_check], 40) : nil),
     Task.new(%w[bin/rake lint:static_verification], 40),
-    Task.new(%w[yarn run lint:tailwind-utils], 20),
     Task.new(%w[bin/rake config_lint], 10),
     Task.new(%w[bin/rake gitlab:sidekiq:all_queues_yml:check], 15),
     (Gitlab.ee? ? Task.new(%w[bin/rake gitlab:sidekiq:sidekiq_queues_yml:check], 11) : nil),
diff --git a/spec/frontend/scripts/frontend/tailwind_lint_against_legacy_utils_spec.js b/spec/frontend/scripts/frontend/tailwind_lint_against_legacy_utils_spec.js
deleted file mode 100644
index d95df0d153be99c7190baeab5fbe2a48d74a16dc..0000000000000000000000000000000000000000
--- a/spec/frontend/scripts/frontend/tailwind_lint_against_legacy_utils_spec.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { lintAgainstLegacyUtils } from '../../../../scripts/frontend/tailwind_lint_against_legacy_utils';
-
-describe('lintAgainstLegacyUtils', () => {
-  beforeEach(() => {
-    jest.spyOn(console, 'log').mockImplementation(jest.fn());
-    jest.spyOn(console, 'warn').mockImplementation(jest.fn());
-  });
-
-  it('does not throw if no legacy utils are used', async () => {
-    await expect(
-      lintAgainstLegacyUtils({ content: [{ raw: '<div class="gl-block">', extension: 'html' }] }),
-    ).resolves.not.toThrow();
-  });
-
-  describe('legacy utils are used', () => {
-    it('does throw on basic legacy utils', async () => {
-      await expect(
-        lintAgainstLegacyUtils({
-          content: [{ raw: '<div class="gl-display-block">', extension: 'html' }],
-        }),
-      ).rejects.toThrow(/You are introducing legacy utilities[\s\S]+\.gl-display-block/gm);
-    });
-
-    it('does throw with modified legacy utils', async () => {
-      await expect(
-        lintAgainstLegacyUtils({
-          content: [{ raw: '<div class="md:gl-display-block">', extension: 'html' }],
-        }),
-      ).rejects.toThrow(/You are introducing legacy utilities[\s\S]+\.md\\:gl-display-block/gm);
-    });
-
-    it('does throw with important legacy utils', async () => {
-      await expect(
-        lintAgainstLegacyUtils({
-          content: [{ raw: '<div class="!gl-display-block">', extension: 'html' }],
-        }),
-      ).rejects.toThrow(/You are introducing legacy utilities[\s\S]+\.\\!gl-display-block/gm);
-    });
-  });
-});
diff --git a/yarn.lock b/yarn.lock
index 6a6afc7c07990815dde38c4ec58caa73368b832e..7b15443a6cac7857cd72ac4044ca1afade5bee00 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1401,10 +1401,10 @@
   resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.119.0.tgz#becbeea7e7ee241baecdad02b9ad04de7a8aed04"
   integrity sha512-Os/PF37pCY75uLA0dmGaZe13BmirzlWH+pFLinCAPRChEC7KhHCJtIy0efRAxzkA4uatmHpJHxftuTc7NeiSNQ==
 
-"@gitlab/ui@98.5.2":
-  version "98.5.2"
-  resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-98.5.2.tgz#7dd8b670c474c3d7e6bf910955c1a3695ffc7496"
-  integrity sha512-5FQgkHat0nIjjxLlygSlhIMSS0hE4VPYOY+WSk4XEQzt9Cqgl/GXsPyjxDMw7o8f90pkCUNNoEiEmf0mZ8bF5w==
+"@gitlab/ui@99.0.0":
+  version "99.0.0"
+  resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-99.0.0.tgz#068f4defb9fe80264d0bd54e32df7b25892b710b"
+  integrity sha512-f5g4dk43ZjqjxSGee44JvB4Nh9m0GybcFEMtLwvgoKQl60oEdLD7hQZv/fh1+IVcXbp0RgOEyy3wsSW9/iItKg==
   dependencies:
     "@floating-ui/dom" "1.4.3"
     echarts "^5.3.2"
@@ -13776,16 +13776,7 @@ string-length@^4.0.1:
     char-regex "^1.0.2"
     strip-ansi "^6.0.0"
 
-"string-width-cjs@npm:string-width@^4.2.0":
-  version "4.2.3"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
-  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
-  dependencies:
-    emoji-regex "^8.0.0"
-    is-fullwidth-code-point "^3.0.0"
-    strip-ansi "^6.0.1"
-
-"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -13838,7 +13829,7 @@ string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1:
   dependencies:
     safe-buffer "~5.1.0"
 
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -13852,13 +13843,6 @@ strip-ansi@^5.2.0:
   dependencies:
     ansi-regex "^4.1.0"
 
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
-  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
-  dependencies:
-    ansi-regex "^5.0.1"
-
 strip-ansi@^7.0.1, strip-ansi@^7.1.0:
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -15583,7 +15567,7 @@ worker-loader@^3.0.8:
     loader-utils "^2.0.0"
     schema-utils "^3.0.0"
 
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -15601,15 +15585,6 @@ wrap-ansi@^6.2.0:
     string-width "^4.1.0"
     strip-ansi "^6.0.0"
 
-wrap-ansi@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
-  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
-  dependencies:
-    ansi-styles "^4.0.0"
-    string-width "^4.1.0"
-    strip-ansi "^6.0.0"
-
 wrap-ansi@^8.1.0:
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"