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
| Archivo | Como actualizar | Riesgo |
|---|---|---|
layouts/section.css | Reemplazar archivo completo | Bajo |
theme/flowtitude.css | Copiar bloques concretos | Medio 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-9xlsiguen existiendo.p-section,py-section,gap-content,gap-blocksiguen generandose.- Las secciones normales tienen media seccion arriba y abajo.
.has-backgroundusa seccion completa..half-topy.half-bottomvuelven 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.