Para dar vida a sus ideas, los desarrolladores web utilizan lenguajes de programación basados en texto, como JavaScript, CSS y HTML.
Ya sabes, abrir un editor y crear una página nueva como esta:
<!DOCTYPE >
<html>
<head>
<meta charset="UTF-8">
<title>Hello, World!</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>Siendo más realistas, utilizas una biblioteca de interfaz de usuario como React y creas un componente reutilizable. Algo así:
import * as React from "react";
export const HelloWorld = () => {
return <h1>Hello, World!</h1>;
};
Tener la habilidad de expresar tus ideas mediante código es un superpoder que te ha costado mucho esfuerzo conseguir. Sin embargo, la programación basada en texto no es la única forma de hacer realidad esas ideas.
Nuestra misión en Webflow es poner estos superpoderes de desarrollo al alcance de todos, pero no todo el mundo quiere escribir símbolos crípticos en un teclado y darse cabezazos contra la pared hasta encontrar ese punto y coma que falta. Hay quienes prefieren materializar sus ideas de forma visual, realizando cambios directamente sobre el resultado y disfrutando de un ciclo de retroalimentación rápido.
El desarrollo basado en texto te obliga a editar el código en una herramienta y luego compilarlo y ejecutarlo por separado para ver cómo quedan los cambios. Es una manipulación indirecta con un ciclo de retroalimentación lento. Incluso con la recarga en caliente más avanzada, si hay un error en el programa, ¡no verás nada más que una excepción!
Así es como se ve un error con la sustitución de módulos en caliente:
Las herramientas de última generación siguen teniendo un ciclo de retroalimentación lento.
Aquí es donde entra en juego el desarrollo visual. Editas los resultados directamente y el ciclo de retroalimentación es inmediato, lo que ofrece una forma más intuitiva de dar vida a las ideas. Incluso si el programa está incompleto, siempre tendrás un resultado con el que trabajar. Así es como se ve la edición visual:
El desarrollo visual proporciona retroalimentación inmediata, siempre.
Para editar un programa a partir de su resultado, necesitas ciertas garantías. Para empezar, siempre debe haber un resultado, incluso si el programa no está completo o tiene errores. Si el programa falla o no se puede ejecutar, ¡no habría forma de arreglarlo! También necesitas poder evaluar piezas independientes de la interfaz de usuario (subárboles) de manera eficiente, o la latencia de ejecutar "todo el programa" genera demasiado retraso para la edición visual. Tampoco puedes tener efectos secundarios sin seguimiento, como la mutación de memoria, porque de lo contrario, al volver a evaluar esos subárboles, se pierde su estado y se producen comportamientos inesperados. Esto es lo que hace que la sustitución de módulos en caliente sea tan inestable en JavaScript. Además, JavaScript tiene todo tipo de "azúcar sintáctico" para desarrolladores de texto que lo convierte en un objetivo difícil para la edición proyectiva.
Para desbloquear el desarrollo visual para la web, tenemos que resolver problemas como estos sin sacrificar la potencia y la flexibilidad de la plataforma web. Queremos que todos tengan los mismos superpoderes que los desarrolladores tradicionales. A menudo se dice que Webflow es una solución "No Code", lo cual es un nombre un poco inapropiado porque nuestros diseñadores hacen una forma de "programación" todos los días, solo que lo hacen visualmente. Ahora, antes de que te pongas pedante y digas <smug-voice> "Eso no es programar de verdad" </smug-voice>, debes saber que nuestros desarrolladores visuales crean componentes, llaman a funciones, declaran variables, definen tipos, crean bucles, realizan llamadas de E/S, crean abstracciones y mucho más... ¡Lo increíble y aleccionador es que nuestros desarrolladores visuales a menudo crean cosas que ni siquiera puedo imaginar cómo hacer con mis habilidades de programación basada en texto!
Para resolver estos problemas, creamos un lenguaje de programación que se escribe visualmente. Lo llamamos Webflow Design Language (WFDL) y es la primera vez que lo compartimos con el mundo.
Aquí tienes un análisis más profundo de qué es y cómo funciona.
¿Qué es el lenguaje de diseño de Webflow?
El lenguaje de diseño de Webflow (WFDL) es un lenguaje de programación diseñado para ser utilizado visualmente. Es un lenguaje de programación puramente funcional, editado de forma proyectiva, que impulsa el diseñador de Webflow con una experiencia de creación visual. En términos aún más informáticos, se asemeja a un cálculo lambda editado estructuralmente con un sistema de tipos Hindley-Milner que admite rangos superiores y polimorfismo de filas.
Tomamos los sitios creados con este lenguaje, los interpretamos y producimos sitios web. Como en el que estás leyendo esto ahora mismo. Aquí tienes un diagrama del flujo de evaluación y renderizado de alto nivel:
Para desglosarlo, te presentaré el WFDL usando la vieja tradición de la programación: ¡"Hola mundo!"
¡Hola mundo!
Vamos a sumergirnos y echar un vistazo a algunas de las funciones que ofrece el Webflow Designer y al WFDL que nuestros clientes (desarrolladores visuales) están creando. Así es como un usuario crea un bloque de texto de "Hola mundo":
Arrastrar un bloque de texto al lienzo del Webflow Designer y actualizarlo para que diga "¡Hola mundo!".
Cuando un usuario arrastra un elemento al lienzo del Webflow Designer de esta manera, creamos el siguiente WFDL para representar el elemento:
<Basic::Block> {
children: [
<Basic::String>
"Hello world!"
</Basic::String>
],
} </Basic::Block>Te estoy mostrando el WFDL proyectado en su sintaxis legible para humanos; sin embargo, nuestras herramientas trabajan directamente con la estructura del Árbol de Sintaxis Abstracta (AST). El WFDL contiene información adicional, como identificadores únicos de ámbito para cada nodo de sintaxis, que se utilizan para generar "mapas de origen" que potencian la edición visual. Los he omitido por brevedad.
Aquí estamos creando dos elementos.
Basic::Blockelemento para representar eldivenvolvente conBasic::Stringproporcionado como hijoBasic::Stringcon"¡Hola mundo!"como su valor
Basic::Block y Basic::String son aquí nombres totalmente cualificados. Esta es una forma elegante de describir un ID único que podemos usar para buscar la implementación subyacente de lo que sea que se esté referenciando. Los nombres cualificados son la forma en que implementamos los espacios de nombres en todo el WFDL.
Basic::Block y Basic::String son también tipos de elementos. Nos indican de qué componentes son instancias estos elementos. Así que aquí tenemos una instancia de Block y una instancia de String proporcionadas por el módulo Basic.
Así es como nuestros desarrolladores visuales añaden nuevos elementos a su sitio: cada elemento representa una instancia de un componente. Para aterrizar esto en algo más familiar, si hiciéramos lo mismo en React como código, se vería algo así:
import { Block } from "./basic";
export const page = () => <Block>Hello world!</Block>;Componentes
Ahora que hemos cubierto los elementos, tenemos la base para algo mucho más interesante: los componentes. Los desarrolladores visuales quieren empaquetar partes reutilizables de su interfaz de usuario y usarlas en todo su sitio. Exploremos cómo se hace esto en el Webflow Designer.
En este video, convertimos nuestro elemento "¡Hola mundo!" en un componente llamado "Hello World". Luego, creamos otra instancia de ese componente justo debajo usando copiar y pegar:
Convertir nuestro elemento en un componente usando la interfaz de creación de componentes.
Cuando el desarrollador visual cambia el componente, todas las instancias se actualizan. Observa cómo, al cambiar el componente de "¡Hola mundo!" a "¡Hola lector!", ambas instancias se actualizan en el lienzo del Webflow Designer.
Editar un componente lo actualiza en todas partes.
Cuando se crea un componente, ocurren dos cosas en el WFDL subyacente:
- Extraemos las partes de la interfaz de usuario que se van a reutilizar en un nuevo componente y exportamos ese componente desde un módulo WFDL.
- Reemplazamos el elemento con un nuevo elemento de instancia de componente que apunta al nuevo componente que extrajimos en el paso 1.
Lo que está sucediendo con el WFDL bajo el capó en este video es una extracción de nuestro Basic::Block a un nuevo componente HelloWorld, que luego exportamos desde nuestro módulo WFDL Site:
// Extract component to a module named Site
// Qualified name Site::HelloWorld is used to access this component
export HelloWorld = {
name: "Hello World",
render: <Basic::Block> {
children: [
<Basic::String>
"Hello world!"
</Basic::String>
],
} </Basic::Block>
}En segundo lugar, reemplazamos el elemento <Basic::Block> que acabamos de extraer a un componente con una instancia que apunta a nuestro nuevo componente, y luego añadimos otra instancia debajo:
- <Basic::Block> {
- children: [
- <Basic::String>
- "Hello world!"
- </Basic::String>
- ],
- } </Basic::Block>
+ <Site::HelloWorld> { } </Site::HelloWorld>
+ <Site::HelloWorld> { } </Site::HelloWorld>Cuando renderizamos un elemento y nos encontramos con el tipo Site::HelloWorld, recuperamos su componente de respaldo del módulo Site, localizamos su exportación HelloWorld, obtenemos su expresión de renderizado y la ejecutamos. En este caso, nuestro árbol inicial Basic::Block -> Basic::String -> "Hello world!".
Si hiciéramos esto en React, extraeríamos nuestro componente a un nuevo módulo:
// Extract component to a new module, site.js
import { Block } from "./basic";
export const HelloWorld = () => <Block>Hello world!</Block>;Y actualizaríamos la página para importar y usar ese componente.
import { HelloWorld } from './site';
<HelloWorld />
<HelloWorld />Propiedades
Uno de los aspectos clave que distingue a un componente de una inclusión o un símbolo es la capacidad de proporcionar props u opciones. Las propiedades describen la interfaz de configuración del componente, lo que permite que cada instancia aporte valores diferentes.
Creación de propiedades en componentes
En este video, el desarrollador visual edita el componente subyacente para añadir una propiedad llamada Children:
Creación de una propiedad de texto en un componente vinculada al campo de texto del elemento.
Este proceso de creación de propiedades implica dos acciones principales:
1. Actualización de la expresión de renderizado: La expresión de renderizado se actualiza para incluir una variable vinculada a la nueva propiedad. Esto permite que el componente renderice contenido de forma dinámica según el valor de la propiedad proporcionada.
2. Creación de un valor predeterminado configurable: Se establece un valor predeterminado extrayendo el contenido anterior y almacenándolo en el tipo. Este valor garantiza que siempre se renderice algo cuando no se proporciona la propiedad.
Los cambios subyacentes en el WFDL de nuestro componente se ven así:
// Site::HelloWorld
{
name: "Hello World",
render: <Basic::Block> {
+ children: data.Children // Now a variable binding!
- <Basic::String>
- "Hello world!"
- </Basic::String>
} </Basic::Block>,
+ // We'll talk about this later, this is a data type.
+ dataType: {
+ Children: {
+ name: 'Children',
+ type: List<Element>
+ defaultValue: [
+ <Basic::String>
+ "Hello world!"
+ </Basic::String>
+ ]
+ }
+ }
}Observa que el elemento "Hello world!" fue extraído y reemplazado por un enlace de variable a data.Children, y el contenido previo se almacena como valor predeterminado.
Un desafío de la manipulación directa es que el programa siempre debe funcionar, ¡o de lo contrario no habrá resultado que editar! Por eso, ante valores faltantes, debemos generar algo. Para gestionar esto, proporcionamos un valor predeterminado o utilizamos huecos tipados como marcadores de posición.
Provisión de valores de propiedad en una instancia de componente
Ahora que el desarrollador ha creado una propiedad para el componente, puede asignar valores para personalizar el comportamiento y la apariencia de una instancia específica. Esto permite adaptar el componente a casos de uso concretos, haciéndolo más dinámico y versátil.
Visualmente, asignar el valor de una propiedad se ve así:
Proporcionar valores distintos para cada instancia actualiza únicamente esa instancia.
Aquí estamos editando las instancias del componente para asignar valores diferentes a cada una. Verás que el panel de configuración a la derecha muestra el valor de la propiedad, el cual también podemos editar directamente en el Webflow Designer.
Los cambios subyacentes en el WFDL se ven así:
<Site::HelloWorld> {
+ Children: [
+ <Basic::String> "Hello friend!" </Basic::String>,
+ ]
} </Site::HelloWorld>
<Site::HelloWorld> {
+ Children: [
+ <Basic::String> "Hello reader!" </Basic::String>,
+ ]
} </Site::HelloWorld>Si esto fuera código de React, implicaría actualizar nuestro componente para incluir una nueva prop tipada, proporcionar un argumento predeterminado y reemplazar el contenido con un identificador children:
import { Block } from "./basic";
export const HelloWorld = ({
children = "Hello world!",
}: {
children: React.ReactNode, // Specifying the type of the children prop
}) => <Block>{children}</Block>;Tipos
Cuando creamos una nueva propiedad, debemos conocer su tipo para poder ofrecer una experiencia de edición visual coherente para estos datos en todo el Webflow Designer.
Cuando creamos y vinculamos la propiedad Children anteriormente, nuestro componente obtuvo un nuevo campo dataType asociado que describe la propiedad:
// I told you we'd talk about it later. Later is now!
dataType: {
Children: {
name: 'Children',
type: List<Element>,
defaultValue: [
<Basic::String>
"Hello world!"
</Basic::String>
]
}
}La creación de una propiedad en el Webflow Designer determina automáticamente su tipo según el contexto en el que se creó. Por ejemplo, si creas un enlace usando el campo "Text" del componente Basic::Block, el Webflow Designer infiere el tipo a partir de ese campo. En este caso, el componente Basic::Block tiene un tipo de datos para la propiedad "Text" definido como List<Element>, por lo que la nueva propiedad también será List<Element>.
Los desarrolladores visuales pueden crear propiedades de diferentes tipos para mejorar sus componentes. Por ejemplo:
- Visibilidad (
List<Boolean>): permite a los desarrolladores controlar si se renderizan elementos específicos según una propiedad. - Imagen (
Record): permite a los desarrolladores subir y proporcionar referencias a imágenes alojadas en una CDN, haciéndolas adaptables automáticamente a través de nuestro panel de Recursos.
En este video, el desarrollador visual ha creado tres propiedades de diferentes tipos:
Tres propiedades de diferentes tipos crean una interfaz de configuración que las refleja.
Lo que produce un tipo de datos de componente que se ve así:
{
Header: {
name: 'Header Text',
type: List<Element>
},
Image: {
name: 'Image',
type: Image // (aliasing Record)
},
Visibility: {
name: 'Show Header',
type: Visibility // (aliasing List<Boolean>)
}
}Los tipos también están calificados y son extensibles mediante módulos WFDL; sin embargo, he omitido los espacios de nombres por brevedad.
Observa cómo, cuando vemos un campo de tipo List<Element>, proporcionamos un cuadro de texto y permitimos la edición en el lienzo del Webflow Designer. Cuando vemos una Imagen, proporcionamos una experiencia de edición de imágenes. Nuestros ingenieros pueden proporcionar cualquier cantidad de tipos con diferentes interfaces como esta, y WFDL los une todos. Dado que toda la interfaz de configuración de Webflow está impulsada por tipos, nuestra experiencia de vinculación de variables dinámicas también se rige por el mismo sistema de tipado.
Por ejemplo, si colocamos nuestro componente Site::HelloWorld dentro de este nuevo componente que ofrece una propiedad List<Element> Header Text, podemos conectarlo a nuestro campo Site::HelloWorld Children porque su tipo también es una propiedad List<Element>:

Ofrecemos esta experiencia ejecutando inferencia de tipos en el ámbito actual junto con un hueco tipado; si el tipo coincide, lo presentamos como una opción para rellenar el hueco mediante un enlace. Los huecos son un tipo de expresión que permite evaluar programas incompletos, lo cual es vital para la edición en vivo. Si la palabra "hueco" te ha pillado por sorpresa y aún tienes curiosidad, te recomiendo leer este artículo. Son requisitos como este los que nos llevaron a crear WFDL como un lenguaje funcional puro, en lugar de utilizar un subconjunto de AST de JavaScript.
Este enfoque de vinculación es conceptualmente similar a cómo un editor de texto proporciona intellisense, solo que un paso más allá. Imagina poder poner un "_" en cualquier parte de tu código y que tu editor te ofrezca un menú desplegable con todos los valores en el ámbito que podrían encajar ahí. Los valores no tienen que provenir exclusivamente de las propiedades del componente, ¡pueden venir de muchas fuentes! Aquí puedes ver el poder de la unificación de tipos presentando opciones de "Blog Posts" proporcionadas por nuestro CMS:

En esta imagen, también notarás que puedes vincular campos de fecha proporcionados por el CMS. Esto sugiere otras características de WFDL relacionadas con la llamada a funciones, implícitos y alias de tipo. WFDL tiene muchas características de este tipo que cierran la brecha entre el código y el no-código; por nombrar algunas:
- Interfaz de funciones externas para interoperar con JavaScript. Esto permite a Webflow proporcionar código que nuestros clientes pueden consumir visualmente. Por ejemplo, funciones de formato de fecha u otros valores de JavaScript.
- Sistema de tipos extensible por el host con huecos tipados e inferencia que permite a nuestro equipo ofrecer interfaces de edición visual personalizadas y únicas, que ofrecen vinculaciones listas para usar con soporte completo para alias de tipo, proporcionando experiencias de edición adaptadas para el mismo tipo subyacente. Una buena forma de visualizar esto es que la experiencia de edición de un número de teléfono sea diferente a la de un correo electrónico, aunque ambos sean, en última instancia, cadenas de texto.
- Evaluación pura que permite muchas de nuestras experiencias interactivas de evaluación y depuración en vivo.
- Huecos que nos permiten admitir la edición en vivo, incluso si los valores faltan o están dañados.
- Reproducción sin ejecutar efectos de nuevo y mucho más.
Nos hemos centrado en cómo WFDL desbloquea el desarrollo visual de front-end y hemos mostrado cómo produce HTML, CSS y JavaScript, pero eso no es todo para lo que lo usamos. WFDL se evalúa en una representación intermedia que convertimos en otros resultados, por ejemplo, una representación JSON del sitio para su uso en la indexación de búsqueda del sitio. También compilamos WFDL en componentes de React, lo que impulsa una función que llamamos DevLink:
También lo usamos para potenciar la indexación de la Búsqueda del sitio:
Un resumen de lo que hemos visto:
Decidimos crear un nuevo lenguaje de programación visual porque editar directamente los árboles de sintaxis abstracta (AST) de JavaScript y JSX no ofrecía las propiedades necesarias para una experiencia visual de primer nivel. Por otro lado, editar una estructura de datos JSON improvisada no brindaba todo el potencial del código. No tomamos esta decisión a la ligera, ya que sabíamos que no era una tarea sencilla.
- Necesitábamos un AST pequeño para la edición proyectiva. El AST de JavaScript es enorme y contiene mucha redundancia debido al azúcar sintáctico y años de código obsoleto, con una gran cantidad de tipos de nodos de sintaxis diferentes.
- La evaluación eficiente de subárboles requiere efectos secundarios controlados sin mutación. A diferencia de JavaScript, WFDL es puro, lo que nos permite ofrecer una reevaluación incremental eficiente.
- JavaScript no tiene solución para estados de programa parciales que permitan la edición proyectiva. WFDL utiliza una combinación de valores predeterminados y huecos tipados para garantizar que siempre tengas un resultado que editar. La edición proyectiva asegura que nunca te encuentres con un error del que no puedas recuperarte.
- Al apuntar a nuestra abstracción, podemos hacer mucho más que renderizar y editar React. Podemos compilar para otros frameworks o incluso plataformas. Todo es analizable estáticamente, lo que crea ricas oportunidades de optimización en tiempo de compilación que no tendríamos con la flexibilidad dinámica de JavaScript.
- El sistema de tipos de TypeScript no garantiza una inferencia de tipos "completa". Necesitábamos una inferencia de tipos completa para determinar qué valores podían presentarse como opción para partes específicas del programa.
- Editar una estructura de árbol JSON simple sin principios de lenguaje de programación no aportaba todo el poder de la programación basada en texto a nuestros desarrolladores visuales.
- Renderizamos todos nuestros sitios en el servidor y la superficie de riesgo de seguridad de JavaScript es enorme. Necesitábamos una solución que nos permitiera limitar el riesgo del usuario sin dejar de conectar el poder del código con FFI. Esto permite a los ingenieros de Webflow aportar valor a los desarrolladores visuales mediante plugins, algo que no pude cubrir aquí.
En Webflow, nuestra misión es poner superpoderes de desarrollo al alcance de todos. Se necesita mucho código para hacer "No Code". Si te interesa saber más sobre WFDL, avísanos o, quién sabe, ¡ven a construir con nosotros!

¡Estamos contratando!
Buscamos talento en producto e ingeniería para que se una a nuestra misión de poner superpoderes de desarrollo al alcance de todos.










