Home / Function/ migrateTheme() — tailwindcss Function Reference

migrateTheme() — tailwindcss Function Reference

Architecture documentation for the migrateTheme() function in migrate-js-config.ts from the tailwindcss codebase.

Entity Profile

Dependency Diagram

graph TD
  0e91912d_5423_6991_60b0_8afbca30e459["migrateTheme()"]
  dc1d7e71_14d1_dd51_5d14_8265f9e59cce["migrateJsConfig()"]
  dc1d7e71_14d1_dd51_5d14_8265f9e59cce -->|calls| 0e91912d_5423_6991_60b0_8afbca30e459
  678b66f4_0a04_0d6e_1925_b3716614324d["resolveConfig()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| 678b66f4_0a04_0d6e_1925_b3716614324d
  c8623640_b79b_0216_79ed_a099482fb629["removeUnnecessarySpacingKeys()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| c8623640_b79b_0216_79ed_a099482fb629
  d4865106_45da_0461_2954_69ab7533f3ba["keyframesToCss()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| d4865106_45da_0461_2954_69ab7533f3ba
  f9776eb1_2e39_1c04_0c09_5a1f1eec4204["buildCustomContainerUtilityRules()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| f9776eb1_2e39_1c04_0c09_5a1f1eec4204
  a9af385a_fd12_f1d8_7cf0_ccb9b281ca18["atRule()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| a9af385a_fd12_f1d8_7cf0_ccb9b281ca18
  0638028e_f2f7_8e77_2f19_1bd2ce4b2d6a["parse()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| 0638028e_f2f7_8e77_2f19_1bd2ce4b2d6a
  303b7e7d_33cb_6a63_7591_f61feb3c4e3d["themeableValues()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| 303b7e7d_33cb_6a63_7591_f61feb3c4e3d
  93c615c1_689e_1732_75cf_439dd95740f6["isValidOpacityValue()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| 93c615c1_689e_1732_75cf_439dd95740f6
  36a174f5_8af8_ad9e_a3e2_7f8dd1ed3d11["createSectionKey()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| 36a174f5_8af8_ad9e_a3e2_7f8dd1ed3d11
  971f7bc7_6ac2_a554_ba52_47038ff41b13["keyPathToCssProperty()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| 971f7bc7_6ac2_a554_ba52_47038ff41b13
  22e232f7_919c_8490_d3d0_2af2fca034d5["escape()"]
  0e91912d_5423_6991_60b0_8afbca30e459 -->|calls| 22e232f7_919c_8490_d3d0_2af2fca034d5
  style 0e91912d_5423_6991_60b0_8afbca30e459 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts lines 97–290

async function migrateTheme(
  designSystem: DesignSystem,
  unresolvedConfig: Config,
  base: string,
): Promise<string> {
  // Resolve the config file without applying plugins and presets, as these are
  // migrated to CSS separately.
  let configToResolve: ConfigFile = {
    base,
    config: { ...unresolvedConfig, plugins: [], presets: undefined },
    reference: false,
    src: undefined,
  }
  let { resolvedConfig, replacedThemeKeys } = resolveConfig(designSystem, [configToResolve])

  let resetNamespaces = new Map<string, boolean>(
    Array.from(replacedThemeKeys.entries()).map(([key]) => [key, false]),
  )

  removeUnnecessarySpacingKeys(designSystem, resolvedConfig, replacedThemeKeys)

  let css = ''
  let prevSectionKey = ''
  let themeSection: string[] = []
  let keyframesCss = ''
  let variants = new Map<string, string>()

  // Special handling of specific theme keys:
  {
    if ('keyframes' in resolvedConfig.theme) {
      keyframesCss += keyframesToCss(resolvedConfig.theme.keyframes)
      delete resolvedConfig.theme.keyframes
    }

    if ('container' in resolvedConfig.theme) {
      let rules = buildCustomContainerUtilityRules(resolvedConfig.theme.container, designSystem)
      if (rules.length > 0) {
        // Using `theme` instead of `utility` so it sits before the `@layer
        // base` with compatibility CSS. While this is technically a utility, it
        // makes a bit more sense to emit this closer to the `@theme` values
        // since it is needed for backwards compatibility.
        css += `\n@tw-bucket theme {\n`
        css += toCss([atRule('@utility', 'container', rules)])
        css += '}\n' // @tw-bucket
      }
      delete resolvedConfig.theme.container
    }

    if ('aria' in resolvedConfig.theme) {
      for (let [key, value] of Object.entries(resolvedConfig.theme.aria ?? {})) {
        // Will be handled by bare values if the names match.
        // E.g.: `aria-foo:flex` should produce `[aria-foo="true"]`
        if (new RegExp(`^${key}=(['"]?)true\\1$`).test(`${value}`)) continue

        // Create custom variant
        variants.set(`aria-${key}`, `&[aria-${value}]`)
      }
      delete resolvedConfig.theme.aria
    }

    if ('data' in resolvedConfig.theme) {
      for (let [key, value] of Object.entries(resolvedConfig.theme.data ?? {})) {
        // Will be handled by bare values if the names match.
        // E.g.: `data-foo:flex` should produce `[data-foo]`
        if (key === value) continue

        // Create custom variant
        variants.set(`data-${key}`, `&[data-${value}]`)
      }
      delete resolvedConfig.theme.data
    }

    if ('supports' in resolvedConfig.theme) {
      for (let [key, value] of Object.entries(resolvedConfig.theme.supports ?? {})) {
        // Will be handled by bare values if the value of the declaration is a
        // CSS variable.
        let parsed = ValueParser.parse(`${value}`)

        // Unwrap the parens, e.g.: `(foo: var(--bar))` → `foo: var(--bar)`
        if (parsed.length === 1 && parsed[0].kind === 'function' && parsed[0].value === '') {
          parsed = parsed[0].nodes
        }

        // Verify structure: `foo: var(--bar)`
        //                    ^^^ ← must match the `key`
        if (
          parsed.length === 3 &&
          parsed[0].kind === 'word' &&
          parsed[0].value === key &&
          parsed[2].kind === 'function' &&
          parsed[2].value === 'var'
        ) {
          continue
        }

        // Create custom variant
        variants.set(`supports-${key}`, `{@supports(${value}){@slot;}}`)
      }
      delete resolvedConfig.theme.supports
    }
  }

  // Convert theme values to CSS custom properties
  for (let [key, value] of themeableValues(resolvedConfig.theme)) {
    if (typeof value !== 'string' && typeof value !== 'number') {
      continue
    }

    if (typeof value === 'string') {
      // This is more advanced than the version in core as ideally something
      // like `rgba(0 0 0 / <alpha-value>)` becomes `rgba(0 0 0)`. Since we know
      // from the `/` that it's used in an alpha channel and we can remove it.
      //
      // In other cases we may not know exactly how its used, so we'll just
      // replace it with `1` like core does.
      value = value.replace(/\s*\/\s*<alpha-value>/, '').replace(/<alpha-value>/, '1')
    }

    // Convert `opacity` namespace from decimal to percentage values.
    // Additionally we can drop values that resolve to the same value as the
    // named modifier with the same name.
    if (key[0] === 'opacity' && (typeof value === 'number' || typeof value === 'string')) {
      let numValue = typeof value === 'string' ? parseFloat(value) : value

      if (numValue >= 0 && numValue <= 1) {
        value = numValue * 100 + '%'
      }

      if (
        typeof value === 'string' &&
        key[1] === value.replace(/%$/, '') &&
        isValidOpacityValue(key[1])
      ) {
        continue
      }
    }

    let sectionKey = createSectionKey(key)
    if (sectionKey !== prevSectionKey) {
      themeSection.push('')
      prevSectionKey = sectionKey
    }

    let property = keyPathToCssProperty(key)

    if (property !== null) {
      if (
        !property.startsWith('default-') &&
        resetNamespaces.has(key[0]) &&
        resetNamespaces.get(key[0]) === false
      ) {
        resetNamespaces.set(key[0], true)
        let ns = keyPathToCssProperty([key[0]])
        if (ns !== null) {
          themeSection.push(`  ${escape(`--${ns}`)}-*: initial;`)
        }
      }

      themeSection.push(`  ${escape(`--${property}`)}: ${value};`)
    }
  }

  if (keyframesCss) {
    themeSection.push('', keyframesCss)
  }

  if (themeSection.length > 0) {
    css += `\n@tw-bucket theme {\n`
    css += `\n@theme {\n`
    css += themeSection.join('\n') + '\n'
    css += '}\n' // @theme
    css += '}\n' // @tw-bucket
  }

  if (variants.size > 0) {
    css += '\n@tw-bucket custom-variant {\n'

    let previousRoot = ''
    for (let [name, selector] of variants) {
      let root = name.split('-')[0]
      if (previousRoot !== root) css += '\n'
      previousRoot = root

      if (selector.startsWith('{')) {
        css += `@custom-variant ${name} ${selector}\n`
      } else {
        css += `@custom-variant ${name} (${selector});\n`
      }
    }
    css += '}\n'
  }

  return css
}

Subdomains

Called By

Frequently Asked Questions

What does migrateTheme() do?
migrateTheme() is a function in the tailwindcss codebase.
What does migrateTheme() call?
migrateTheme() calls 11 function(s): atRule, buildCustomContainerUtilityRules, createSectionKey, escape, isValidOpacityValue, keyPathToCssProperty, keyframesToCss, parse, and 3 more.
What calls migrateTheme()?
migrateTheme() is called by 1 function(s): migrateJsConfig.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free