Referencia

Migracion CSS math fallback

Guia para actualizar FDS con fallbacks de tipografia y spacing sin perder cambios propios en los tokens.

Esta migracion separa dos cosas que antes estaban mezcladas:

  • Los knobs que cada proyecto puede personalizar: --ft-text-*, --ft-space-*, colores, botones, cards, container.
  • Los valores derivados que FDS genera desde esos knobs: --text-*, --spacing-*, --spacing-section, --spacing-hero.

La regla importante: no reemplaces todo theme/flowtitude.css si tu proyecto ya tiene tokens personalizados. Migra solo los bloques indicados.


Resumen rapido

ArchivoComo actualizarRiesgo
layouts/section.cssReemplazar archivo completoBajo
theme/flowtitude.cssCopiar bloques concretosMedio si se reemplaza entero

layouts/section.css puede reemplazarse entero porque no contiene knobs de marca. theme/flowtitude.css no deberia reemplazarse entero en instalaciones personalizadas: ahi suelen vivir colores, escalas, tamanos de card, botones y ajustes de spacing.


Antes de empezar

Haz una copia del archivo actual:

cp theme/flowtitude.css theme/flowtitude.css.backup
cp layouts/section.css layouts/section.css.backup

Despues identifica si tu proyecto modifico alguno de estos knobs:

--ft-text-min
--ft-text-max
--ft-type-step-min
--ft-type-step-max
--ft-text-factor

--ft-space-min
--ft-space-max
--ft-space-step-min
--ft-space-step-max
--ft-space-factor

--ft-space-factor-inner
--ft-space-factor-content
--ft-space-factor-stack
--ft-space-factor-block
--ft-space-factor-columns
--ft-space-factor-section
--ft-space-factor-hero

Si no tocaste esos knobs, puedes usar los fallbacks estaticos por defecto. Si los tocaste, conserva tus knobs y recalcula los fallbacks para que los navegadores antiguos se parezcan mas a tu configuracion.


Paso 1: reemplazar sections.css

Este archivo se puede reemplazar completo. El cambio clave es que ya no calcula media seccion dentro del layout:

section {
  @apply @container w-full px-[var(--ft-padding-content-x)];
  padding-top: var(--ft-padding-content-y-half);
  padding-bottom: var(--ft-padding-content-y-half);
}

Y las clases semanticas tambien consumen el mismo token:

section.half-top,
section.half-top:first-of-type:not(.has-background) {
  padding-top: var(--ft-padding-content-y-half);
}

section.half-bottom,
section.half-bottom:last-of-type:not(.has-background) {
  padding-bottom: var(--ft-padding-content-y-half);
}

Asi sections.css deja de depender de calc(var(--ft-padding-content-y) / 2). El calculo vive en theme/flowtitude.css, donde puede tener fallback.


Paso 2: no reemplazar tus knobs

En theme/flowtitude.css, conserva tu bloque de configuracion:

@layer base {
  :root {
    /* RANGES / KNOBS */
    --viewport-min: 410px;
    --viewport-max: 1280px;

    --ft-text-min: 1rem;
    --ft-text-max: 1.125rem;
    --ft-type-step-min: 1.125;
    --ft-type-step-max: 1.2;
    --ft-text-factor: 1;

    --ft-space-min: 0.5rem;
    --ft-space-max: 1.5rem;
    --ft-space-step-min: 1.3;
    --ft-space-step-max: 1.5;
    --ft-space-factor: 1;
  }
}

No copies este bloque desde una version nueva si tu proyecto lo tiene personalizado. Esta es la parte que debe sobrevivir.


Paso 3: ajustar layout defaults

Dentro del primer :root, cambia solo la zona de layout defaults.

Antes:

--ft-container: min(var(--container-7xl), 90%);
--ft-padding-content-x: 1rem;
--ft-padding-content-y: var(--spacing-section);
--ft-gap-content: var(--spacing-block);

Despues:

--ft-container: 90%;
--ft-padding-content-x: 1rem;
--ft-padding-content-y: var(--spacing-section);
--ft-padding-content-y-half: 1.2675rem;
--ft-gap-content: var(--spacing-block);

Por que cambia --ft-container: el fallback base evita min() para navegadores con CSS math limitado. La version moderna se reintroduce dentro de @supports.

Si tu proyecto tenia un container personalizado, separalo asi:

/* Fallback sin min()/calc() */
--ft-container: 90%;

Y dentro del bloque @supports, usa tu valor moderno:

--ft-container: min(var(--container-7xl), 90%);

Paso 4: reemplazar derived values por fallbacks simples

En el primer :root, sustituye el bloque antiguo DERIVED VALUES por este:

/* ============================================================
   2) DERIVED VALUE FALLBACKS (no calc/clamp required)
   ============================================================ */

--ft-fluid-t: 0;
--ft-text-value: var(--ft-text-min);
--ft-type-step: var(--ft-type-step-min);
--ft-space-value: var(--ft-space-min);
--ft-space-step: var(--ft-space-step-min);

Esto permite que los navegadores que no entienden los calculos fluidos sigan teniendo valores validos.


Paso 5: reemplazar la zona de text y spacing en @theme

Dentro de @theme, reemplaza la escala tipografica y de spacing calculada por fallbacks estaticos.

La zona a reemplazar empieza aqui:

/* Typography scale */

Y termina en:

--spacing-hero: ...;

Nuevo bloque por defecto:

/* Typography scale (static fallback; fluid override below) */
--text-base: 1rem;
--text-sm: 0.888889rem;
--text-xs: 0.790123rem;
--text-lg: 1.125rem;
--text-xl: 1.265625rem;
--text-2xl: 1.423828rem;
--text-3xl: 1.601807rem;
--text-4xl: 1.802032rem;
--text-5xl: 2.027287rem;
--text-6xl: 2.280697rem;
--text-7xl: 2.565785rem;
--text-8xl: 2.886508rem;
--text-9xl: 3.247321rem;

/* Spacing scale */
--spacing: 0.25rem;

--spacing-fluid-md: 0.5rem;
--spacing-fluid-sm: 0.384615rem;
--spacing-fluid-xs: 0.295858rem;
--spacing-fluid-lg: 0.65rem;
--spacing-fluid-xl: 0.845rem;
--spacing-fluid-2xl: 1.0985rem;

/* Semantic spacing (static fallback; fluid override below) */
--spacing-inner: 0.147929rem;
--spacing-content: 0.288462rem;
--spacing-stack: 0.384615rem;
--spacing-block: 0.5rem;
--spacing-columns: 0.75rem;
--spacing-section: 2.535rem;
--spacing-hero: 4.94325rem;

Estos valores son fallbacks mobile/base. En navegadores modernos seran sobrescritos por el bloque fluido de @supports.


Paso 6: anadir desktop fallback y mejora fluida

Justo despues de cerrar @theme, anade este bloque completo:

@layer base {
  /* Static desktop fallback for browsers with custom properties but weak CSS math support. */
  @media (min-width: 1280px) {
    :root {
      --text-base: 1.125rem;
      --text-sm: 0.9375rem;
      --text-xs: 0.78125rem;
      --text-lg: 1.35rem;
      --text-xl: 1.62rem;
      --text-2xl: 1.944rem;
      --text-3xl: 2.3328rem;
      --text-4xl: 2.79936rem;
      --text-5xl: 3.359232rem;
      --text-6xl: 4.031078rem;
      --text-7xl: 4.837294rem;
      --text-8xl: 5.804753rem;
      --text-9xl: 6.965703rem;

      --spacing-fluid-md: 1.5rem;
      --spacing-fluid-sm: 1rem;
      --spacing-fluid-xs: 0.666667rem;
      --spacing-fluid-lg: 2.25rem;
      --spacing-fluid-xl: 3.375rem;
      --spacing-fluid-2xl: 5.0625rem;

      --spacing-inner: 0.333333rem;
      --spacing-content: 0.75rem;
      --spacing-stack: 1rem;
      --spacing-block: 1.5rem;
      --spacing-columns: 2.25rem;
      --spacing-section: 10.125rem;
      --spacing-hero: 22.78125rem;
      --ft-padding-content-y-half: 5.0625rem;
    }
  }

  /*
    Fluid enhancement.
    Kept behind @supports so browsers that fail calc(), clamp(), division,
    or nested math functions keep the static fallback tokens above.
  */
  @supports (font-size: calc(clamp(1rem, 2vw, 2rem) * 1)) and (padding-top: calc(1rem / 2)) and (width: min(100%, 1rem)) {
    :root {
      --ft-container: min(var(--container-7xl), 90%);
      --ft-padding-content-y-half: calc(var(--ft-padding-content-y) / 2);

      --ft-fluid-t: calc(
        (100vw - var(--viewport-min)) /
        (var(--viewport-max) - var(--viewport-min))
      );

      --ft-text-value: clamp(
        var(--ft-text-min),
        calc(var(--ft-text-min) + (var(--ft-text-max) - var(--ft-text-min)) * var(--ft-fluid-t)),
        var(--ft-text-max)
      );

      --ft-type-step: clamp(
        var(--ft-type-step-min),
        calc(var(--ft-type-step-min) + (var(--ft-type-step-max) - var(--ft-type-step-min)) * var(--ft-fluid-t)),
        var(--ft-type-step-max)
      );

      --ft-space-value: clamp(
        var(--ft-space-min),
        calc(var(--ft-space-min) + (var(--ft-space-max) - var(--ft-space-min)) * var(--ft-fluid-t)),
        var(--ft-space-max)
      );

      --ft-space-step: clamp(
        var(--ft-space-step-min),
        calc(var(--ft-space-step-min) + (var(--ft-space-step-max) - var(--ft-space-step-min)) * var(--ft-fluid-t)),
        var(--ft-space-step-max)
      );

      --text-base: calc(var(--ft-text-value) * var(--ft-text-factor));
      --text-sm: calc(var(--text-base) / var(--ft-type-step));
      --text-xs: calc(var(--text-sm) / var(--ft-type-step));
      --text-lg: calc(var(--text-base) * var(--ft-type-step));
      --text-xl: calc(var(--text-lg) * var(--ft-type-step));
      --text-2xl: calc(var(--text-xl) * var(--ft-type-step));
      --text-3xl: calc(var(--text-2xl) * var(--ft-type-step));
      --text-4xl: calc(var(--text-3xl) * var(--ft-type-step));
      --text-5xl: calc(var(--text-4xl) * var(--ft-type-step));
      --text-6xl: calc(var(--text-5xl) * var(--ft-type-step));
      --text-7xl: calc(var(--text-6xl) * var(--ft-type-step));
      --text-8xl: calc(var(--text-7xl) * var(--ft-type-step));
      --text-9xl: calc(var(--text-8xl) * var(--ft-type-step));

      --spacing-fluid-md: calc(var(--ft-space-value) * var(--ft-space-factor));
      --spacing-fluid-sm: calc(var(--spacing-fluid-md) / var(--ft-space-step));
      --spacing-fluid-xs: calc(var(--spacing-fluid-sm) / var(--ft-space-step));
      --spacing-fluid-lg: calc(var(--spacing-fluid-md) * var(--ft-space-step));
      --spacing-fluid-xl: calc(var(--spacing-fluid-lg) * var(--ft-space-step));
      --spacing-fluid-2xl: calc(var(--spacing-fluid-xl) * var(--ft-space-step));

      --spacing-inner: calc(var(--spacing-fluid-xs) * var(--ft-space-factor-inner));
      --spacing-content: calc(var(--spacing-fluid-sm) * var(--ft-space-factor-content));
      --spacing-stack: calc(var(--spacing-fluid-sm) * var(--ft-space-factor-stack));
      --spacing-block: calc(var(--spacing-fluid-md) * var(--ft-space-factor-block));
      --spacing-columns: calc(var(--spacing-fluid-md) * var(--ft-space-factor-columns));
      --spacing-section: calc(var(--spacing-fluid-xl) * var(--ft-space-factor-section));
      --spacing-hero: calc(var(--spacing-fluid-2xl) * var(--ft-space-factor-hero));
    }
  }
}

Si personalizaste --ft-container, cambia solo esta linea dentro de @supports:

--ft-container: min(var(--container-7xl), 90%);

Si tus knobs son personalizados

Los navegadores modernos usaran tus knobs porque el bloque @supports vuelve a calcular todo con calc() y clamp().

Los navegadores con soporte limitado usaran los fallbacks estaticos. Para que esos fallbacks reflejen tu escala personalizada, recalcula los valores base y desktop con las mismas formulas.

Formulas mobile/base

text-base = ft-text-min * ft-text-factor
text-sm   = text-base / ft-type-step-min
text-xs   = text-sm / ft-type-step-min
text-lg   = text-base * ft-type-step-min
text-xl   = text-lg * ft-type-step-min
...

spacing-fluid-md  = ft-space-min * ft-space-factor
spacing-fluid-sm  = spacing-fluid-md / ft-space-step-min
spacing-fluid-xs  = spacing-fluid-sm / ft-space-step-min
spacing-fluid-lg  = spacing-fluid-md * ft-space-step-min
spacing-fluid-xl  = spacing-fluid-lg * ft-space-step-min
spacing-fluid-2xl = spacing-fluid-xl * ft-space-step-min

spacing-inner   = spacing-fluid-xs * ft-space-factor-inner
spacing-content = spacing-fluid-sm * ft-space-factor-content
spacing-stack   = spacing-fluid-sm * ft-space-factor-stack
spacing-block   = spacing-fluid-md * ft-space-factor-block
spacing-columns = spacing-fluid-md * ft-space-factor-columns
spacing-section = spacing-fluid-xl * ft-space-factor-section
spacing-hero    = spacing-fluid-2xl * ft-space-factor-hero
padding-y-half  = spacing-section / 2

Formulas desktop

Usa las mismas formulas, pero sustituyendo:

ft-text-min       -> ft-text-max
ft-type-step-min  -> ft-type-step-max
ft-space-min      -> ft-space-max
ft-space-step-min -> ft-space-step-max

Snippet para recalcular fallbacks

Puedes pegar esto en la consola del navegador o en Node para obtener los valores CSS. Cambia los knobs al principio.

const knobs = {
  textMin: 1,
  textMax: 1.125,
  typeStepMin: 1.125,
  typeStepMax: 1.2,
  textFactor: 1,

  spaceMin: 0.5,
  spaceMax: 1.5,
  spaceStepMin: 1.3,
  spaceStepMax: 1.5,
  spaceFactor: 1,

  factorInner: 0.5,
  factorContent: 0.75,
  factorStack: 1,
  factorBlock: 1,
  factorColumns: 1.5,
  factorSection: 3,
  factorHero: 4.5,
}

function round(value) {
  return Number(value.toFixed(6))
}

function rem(value) {
  return `${round(value)}rem`
}

function scale(kind) {
  const isMax = kind === 'desktop'
  const textBase = (isMax ? knobs.textMax : knobs.textMin) * knobs.textFactor
  const typeStep = isMax ? knobs.typeStepMax : knobs.typeStepMin
  const spaceBase = (isMax ? knobs.spaceMax : knobs.spaceMin) * knobs.spaceFactor
  const spaceStep = isMax ? knobs.spaceStepMax : knobs.spaceStepMin

  const text = { base: textBase }
  text.sm = text.base / typeStep
  text.xs = text.sm / typeStep

  let prev = text.base
  for (const key of ['lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl', '8xl', '9xl']) {
    prev *= typeStep
    text[key] = prev
  }

  const fluid = { md: spaceBase }
  fluid.sm = fluid.md / spaceStep
  fluid.xs = fluid.sm / spaceStep
  fluid.lg = fluid.md * spaceStep
  fluid.xl = fluid.lg * spaceStep
  fluid['2xl'] = fluid.xl * spaceStep

  const semantic = {
    inner: fluid.xs * knobs.factorInner,
    content: fluid.sm * knobs.factorContent,
    stack: fluid.sm * knobs.factorStack,
    block: fluid.md * knobs.factorBlock,
    columns: fluid.md * knobs.factorColumns,
    section: fluid.xl * knobs.factorSection,
    hero: fluid['2xl'] * knobs.factorHero,
  }

  return { text, fluid, semantic, half: semantic.section / 2 }
}

for (const [label, values] of Object.entries({ base: scale('base'), desktop: scale('desktop') })) {
  console.log(`/* ${label} */`)
  for (const [key, value] of Object.entries(values.text)) {
    console.log(`--text-${key}: ${rem(value)};`)
  }
  for (const [key, value] of Object.entries(values.fluid)) {
    console.log(`--spacing-fluid-${key}: ${rem(value)};`)
  }
  for (const [key, value] of Object.entries(values.semantic)) {
    console.log(`--spacing-${key}: ${rem(value)};`)
  }
  console.log(`--ft-padding-content-y-half: ${rem(values.half)};`)
}

Checklist de verificacion

Despues de migrar:

npm run build --prefix docs

Y, si tienes Tailwind CLI instalado en el proyecto:

node_modules/.bin/tailwindcss -i main.css -o /tmp/fds-migration-check.css

Comprueba en navegador:

  • text-base, text-4xl, text-9xl siguen existiendo.
  • p-section, py-section, gap-content, gap-block siguen generandose.
  • Las secciones normales tienen media seccion arriba y abajo.
  • .has-background usa seccion completa.
  • .half-top y .half-bottom vuelven a media seccion.

Que no cambia

No cambian las clases publicas:

text-*
p-*
py-section
gap-content
gap-block
gap-columns
section.has-background
section.half-top
section.half-bottom

La migracion cambia la arquitectura interna de tokens, no la API de clases.

Previous
Integracion WordPress