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
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19["migrateTheme()"]
  8d0812d4_61e4_e939_916d_a33e94faf43f["migrateJsConfig()"]
  8d0812d4_61e4_e939_916d_a33e94faf43f -->|calls| 77d1d37f_6021_dc8c_6d04_0c3b07e3ad19
  6b5865d6_780d_1bbb_5df1_7ecca0e9b1a1["resolveConfig()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| 6b5865d6_780d_1bbb_5df1_7ecca0e9b1a1
  abaa1198_ca17_6d7e_fe6d_a70c79ad1e3f["removeUnnecessarySpacingKeys()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| abaa1198_ca17_6d7e_fe6d_a70c79ad1e3f
  f7274245_f1a4_d8b1_30d8_a1497768c1d4["keyframesToCss()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| f7274245_f1a4_d8b1_30d8_a1497768c1d4
  6e906b7f_b503_9163_425a_c68220491669["buildCustomContainerUtilityRules()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| 6e906b7f_b503_9163_425a_c68220491669
  c35acfc6_964d_737e_6ecc_275e6f10293a["atRule()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| c35acfc6_964d_737e_6ecc_275e6f10293a
  e5eb2faf_45a2_ac47_3404_8bd4e7eb6817["parse()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| e5eb2faf_45a2_ac47_3404_8bd4e7eb6817
  3e33c90a_e618_0b36_8589_88cf333c1482["themeableValues()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| 3e33c90a_e618_0b36_8589_88cf333c1482
  3962ad41_d147_715e_98ef_72bf2be093d9["isValidOpacityValue()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| 3962ad41_d147_715e_98ef_72bf2be093d9
  585e2570_70ea_9c34_2ea1_950e5cc89977["createSectionKey()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| 585e2570_70ea_9c34_2ea1_950e5cc89977
  f8dcf272_aab9_d786_7975_56fd563c0739["keyPathToCssProperty()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| f8dcf272_aab9_d786_7975_56fd563c0739
  7cf1fe2b_69a4_05dc_ae13_d3eebe4e10fc["escape()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| 7cf1fe2b_69a4_05dc_ae13_d3eebe4e10fc
  e7a34553_0273_6202_4792_07409e33d8f0["toCss()"]
  77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 -->|calls| e7a34553_0273_6202_4792_07409e33d8f0
  style 77d1d37f_6021_dc8c_6d04_0c3b07e3ad19 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
}

Domain

Subdomains

Called By

Frequently Asked Questions

What does migrateTheme() do?
migrateTheme() is a function in the tailwindcss codebase.
What does migrateTheme() call?
migrateTheme() calls 12 function(s): atRule, buildCustomContainerUtilityRules, createSectionKey, escape, isValidOpacityValue, keyPathToCssProperty, keyframesToCss, parse, and 4 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