Lenguajes - meme HTML

Para facilitar la programación y el mantenimiento de nuestro software, en el proyecto de meme.js propongo 3 lenguajes con los que pretendo facilitar el uso de HTML, CSS, JavaScript y su interoperabilidad.

Con estos lenguajes, logramos obtener funcionalidades completamente nuevas, con una gran potencia y robustez, aunado a una facilidad de modularización e independencia que no podrás creer.

Los lenguajes que se proponen en meme.js son: meme-html, meme-css y meme-js, los tres son lenguajes que simplifican y enriquecen HTML, CSS y JS.

En este artículo pretendo mostrarte las bases de los lenguajes, para que puedas comenzar a desenvolverse con meme.js y todas las innovaciones que trae consigo.

Introducción

La simplificación que ofrece meme-css (en adelante mc), es más para obtener una sintonía con mh y algunas características del SDK de meme.js. Por esto mismo los cambios son mínimos con respecto a CSS y salvo algunas comodidades que hay en mc, prácticamente estaremos escribiendo CSS todo el tiempo.

Los puntos que tenemos que tener en cuenta al usar mc, son:

  1. Debemos dar por hecho que al escribir mc, estamos escribiendo código CSS simplificado, por lo que todas las propiedades y selectores, que ya usas en CSS son completamente válidos.
  2. La forma en que el compilador es capaz de diferenciar entre propiedades y selectores es el carácter de dos puntos (:), por lo que toda línea que tenga dos puntos solos (\b:\b), será una propiedad, todo lo demás, se considera un selector.
  3. Ya que no podemos escribir dos puntos solos (\b:\b), en los selectores, los pseudo elementos se escribirán con dobles dos puntos (::).
  4. mc, no pretende de ninguna forma añadir funcionalidades de programación al lenguaje de CSS, por esto mismo, no esperes tener estructuras de control que te ayuden a escribir CSS. El compilador de meme.js, lo único que hace, es una transpilacion simple a CSS y nada más.
  5. En mc se reserva el uso de las llaves ({ y }), para la escritura de los Script's de compilación, por lo que no se necesitan al escribir mc.
  6. Considera que mc, es un lenguaje de indentación significativa, por lo que la indentación es parte de cómo declaramos el parentesco entre las propiedades y los selectores.
  7. En mc hay una instrucción por línea, y consideraremos como instrucción un selector o propiedad, que empieza con la instrucción y termina con un salto de línea (\r o \n), o carácter de finalización.

A grandes rasgos, a CSS solo le estamos quitando las llaves ({ y }) y el punto y coma (;), para terminar una instrucción es completamente opcional.

Comentarios

En mc, tenemos los comentarios JS de toda la vida. Para comentarios de una sola línea utilizaremos la ya clásica doble barra (//), y para comentarios multilínea usaremos el igualmente clásico barra y asterisco (/*), para iniciar un comentario multilínea y asterisco y barra (*/), para concluirlo. Con esto logramos que mc se complemente perfectamente con meme-html y meme-js.

Relación entre instrucciones

Al igual que en CSS, tenemos instrucciones que son hijas y hermanas, en CSS un selector es una instrucción madre, pues esta puede contener una o muchas propiedades, las cuales son instrucciones hijas. Y a su vez, este mismo selector es hermana de otras instrucciones (otros selectores o propiedades). Lo que indica esta relación, es la sangría de cada una, así como la posición de la instrucción en el documento, por ejemplo:

.padre-1
	display: flex
	justify-content: center
	>.hija-1
		align-items: center
		>.hija-de-hija-1
			position: absolute
			width: 100%
	>.hermana-de-hija-1
		position: absolute

Por favor, nota que en el ejemplo anterior no hay llaves ni puntos y comas, ya que como lo mencionamos anteriormente, estos son caracteres que se reservan para otro propósito, por eso mismo, no se usan como normalmente se usarían en CSS. Por supuesto, esto también simplifica la sintaxis.

Propiedades

Al igual que en CSS, una propiedad es una instrucción que describe un estilo en concreto, dado que son las mismas que se escribirían en CSS, me limitaré a describir sólo las partes y peculiaridades que tienen las propiedades en mc:

  1. Nombre de propiedad: El nombre es un conjunto de caracteres sin espacios intermedios, que pueden ser números, letras y/o los caracteres -, _.
  2. Separador de propiedad: todas las propiedades se separan con un solo carácter de dos puntos solo (\b:\b), este carácter puede tener espacios de línea ( o \t), que se necesiten, tanto antes como después.
  3. Valor de la propiedad: cualquier carácter posterior a dos puntos (:), se considerará como parte de los valores de la propiedad. Excepto por cualquier carácter como puede ser un salto de línea o carácter especial de terminación.

ejemplo:

border: solid 1 rgba( 0, 0, 0, .5 )

Variables CSS

En mc, se facilita el uso de las variables CSS, estas pueden consumirse anteponiendo el carácter $ a el nombre de la variable, si necesitamos establecer valores por defecto, debemos indicarlo con el carácter pipe (|), inmediatamente después del nombre y a continuación el valor por defecto que se usará, por ejemplo:

::root
	--color-ejemplo-1: red
	--color-ejemplo-2: blue
.ejemplo
	color: $color-ejemplo-1
	background: $color-ejemplo-2|green

Transpila a:

:root {
	--color-ejemplo-1: red;
	--color-ejemplo-2: blue;
}
.ejemplo {
	color: var(--color-ejemplo-1);
	background: var(--color-ejemplo-2, green);
}

Cabe mencionar que en mc, tanto los estilos como las variables CSS tienen un alcance limitado, con lo que tendremos dos tipos de variables CSS, las de alcance local, que solo afectarán a nuestro componente y las de uso global que afectan a todo el proyecto.

Por defecto todas las variables que usamos son de alcance local, por eso para declararlas lo hacemos con los guiones al inicio como en el estándar y para su consumo solo usamos el carácter $, pero en caso de que necesitáramos declarar y usar variables de alcance global, anteponemos el carácter !, tanto en la declaración como en el uso, por ejemplo:

::root
	--variable_local: red
	--!variable_global: blue
.ejemplo
	color: $variable_local
	background: $!variable_global|green

Números con escala ajustable

Así le llamo a todos los números enteros que no tienen una unidad CSS definida por el programador, y la propiedad a la que pertenece, se encuentra en la lista de propiedades reservadas, y a estos números se les colocará una escala especial, una vez que se compilen los archivos y según se establezcan en las configuraciones del proyecto, concretamente la propiedad: css_auto_unit, por ejemplo:

.ejemplo
	border: solid 1 red

Transpila a:

.ejemplo {
	border: solid 1px red;
}
En este caso el número con escala ajustable es el 1, al no tener una escala definida por nosotros y al estar la propiedad border en la lista de propiedades reservadas, el compilador la transpilara a 1px si tenemos la configuración por defecto en el proyecto. O por ejemplo podría transpilarla a:
border: solid calc( ( 1 * 100vw ) / var(--xs) ) red;
en caso de que hayamos ajustado la propiedad css_auto_unit con el valor auto-viewport-width. Igualmente podríamos ajustar la propiedad css_auto_unit a cualquier unidad de CSS válida y el compilador transpilaria todos los números con escala ajustable a números con esa unidad.

Cabe mencionar que esta característica es solo para facilitar la escritura y mantenimiento de nuestro código, y es completamente opcional, no tienes por que usarla si no estas agusto con ella.

Propiedades reservadas

Las propiedades reservadas, son aquellas en las que los números sin unidad CSS, son reemplazados por valores calculados, según se indique en la configuración, concretamente la propiedad css_auto_unit.

Las propiedades reservadas, son:

[
	"-webkit-text-stroke"        ,
	"background-position"        ,
	"background-position-x"      ,
	"background-position-y"      ,
	"background-size"            ,
	"block-size"                 ,
	"border"                     ,
	"border-block"               ,
	"border-block-end"           ,
	"border-block-end-width"     ,
	"border-block-start"         ,
	"border-block-start-width"   ,
	"border-block-width"         ,
	"border-bottom"              ,
	"border-bottom-left-radius"  ,
	"border-bottom-right-radius" ,
	"border-bottom-width"        ,
	"border-inline"              ,
	"border-inline-end"          ,
	"border-inline-start"        ,
	"border-inline-width"        ,
	"border-left"                ,
	"border-left-width"          ,
	"border-radius"              ,
	"border-right"               ,
	"border-right-width"         ,
	"border-spacing"             ,
	"border-top"                 ,
	"border-top-left-radius"     ,
	"border-top-right-radius"    ,
	"border-width"               ,
	"bottom"                     ,
	"box-shadow"                 ,
	"clip"                       ,
	"column-gap"                 ,
	"column-rule-width"          ,
	"column-width"               ,
	"flex-basis"                 ,
	"font-size"                  ,
	"gap"                        ,
	"grid"                       ,
	"grid-gap"                   ,
	"grid-template"              ,
	"height"                     ,
	"inline-size"                ,
	"inset"                      ,
	"inset-block"                ,
	"inset-block-end"            ,
	"inset-block-start"          ,
	"inset-inline"               ,
	"inset-inline-end"           ,
	"inset-inline-start"         ,
	"left"                       ,
	"letter-spacing"             ,
	"margin"                     ,
	"margin-block"               ,
	"margin-block-end"           ,
	"margin-bottom"              ,
	"margin-inline"              ,
	"margin-inline-end"          ,
	"margin-inline-start"        ,
	"margin-left"                ,
	"margin-right"               ,
	"margin-top"                 ,
	"mask-size"                  ,
	"max-block-size"             ,
	"max-height"                 ,
	"max-inline-size"            ,
	"max-width"                  ,
	"min-block-size"             ,
	"min-height"                 ,
	"min-inline-size"            ,
	"min-width"                  ,
	"offset"                     ,
	"offset-anchor"              ,
	"offset-distance"            ,
	"outline"                    ,
	"outline-offset"             ,
	"outline-width"              ,
	"padding"                    ,
	"padding-block"              ,
	"padding-block-end"          ,
	"padding-block-start"        ,
	"padding-bottom"             ,
	"padding-inline"             ,
	"padding-inline-end"         ,
	"padding-inline-start"       ,
	"padding-left"               ,
	"padding-right"              ,
	"padding-top"                ,
	"perspective"                ,
	"right"                      ,
	"row-gap"                    ,
	"scroll-padding"             ,
	"scroll-padding-block"       ,
	"scroll-padding-block-end"   ,
	"scroll-padding-block-start" ,
	"scroll-padding-bottom"      ,
	"scroll-padding-inline"      ,
	"scroll-padding-inline-end"  ,
	"scroll-padding-inline-start",
	"scroll-padding-left"        ,
	"scroll-padding-right"       ,
	"scroll-padding-top"         ,
	"scroll-snap-coordinate"     ,
	"scroll-snap-destination"    ,
	"shape-margin"               ,
	"stroke-width"               ,
	"text-shadow"                ,
	"text-stroke"                ,
	"top"                        ,
	"width"                      ,
	"word-spacing"               ,
]

Relación en el nodo

En cuanto a las relaciones con otras instrucciones, una propiedad no puede tener hijas, una propiedad puede no tener ninguna instrucción padre. En caso de no tener padre, la propiedad es aplicada al componente como tal (host).

En caso de que una propiedad tenga mayor sangría que otra propiedad, la instrucción padre de ambas, será el selector inmediato con menor sangría.

La sangría entre instrucciones puede ser espacios o tabulaciones e incluso una combinación de ambas, solo se considera el número de espacios o tabulaciones.

Selector

Como ya mencione antes, todo lo que no sea considerado una propiedad, se considerará un selector, y ya que los selectores se escriben igual que en CSS, solo me limitaré a describir sus peculiaridades en mc.

pseudo-elementos

Al no poder usar los dos puntos solos en un selector, para el uso de los pseudo-elementos se escribirán con dobles dos puntos (::), en caso de que un pseudo-elemento se escriba con doble dos puntos, se requiere se escriban, cuatro dos puntos (::::), por ejemplo:

>.ejemplo
	::after
	::::selection

Atributos

Los atributos de selector, se escriben exactamente igual que en CSS, salvo que es una de las instrucciones multi línea debido a que contiene un carácter de inicio ([), y un carácter final (]), y cabe la posibilidad de escribir varios atributos de una sola vez, por ejemplo:

a[title]
a[href="https:"]
a[href*=example1]
a[href$=.org]
a[class=logo]
a[
	href$=.org
	class= logo
]

Transpila a:

a[title] {}
a[href="https:"] {}
a[href*=example1] {}
a[href$=.org] {}
a[class=logo] {}
a[href$=.org][class= logo] {}

Operador de ampersand (&)

Este operador se interpreta como una representación de un selector padre, y se usa cuando necesitamos que un selector padre se vea afectado por un selector hijo, por ejemplo:

.padre
	background-color: red
	&.azul
		background-color: blue

Transpila a:

.padre {
	background-color: red;
}
.padre.azul {
	background-color: blue;
}

Operador de negación (!)

Este operador nos permite ignorar cualquiera de las instrucciones padres, dejando únicamente la instrucción que contenga este operador. Podemos ocupar este operador para cuando queremos que un selector sea global, por ejemplo:

.padre
	background-color: red
	.azul, !.azul-global
		background-color: blue

Transpila a:

.padre {
	background-color: red;
}
.padre .azul, .azul-global {
	background-color: blue;
}

Recordemos que igual que las variables css, todos los estilos son de alcance local, por lo que si en algún momento, tenemos la necesidad de colocar estilos globales, usaremos este operador.

Relación en el nodo

En cuanto a las relaciones con otras instrucciones, un selector puede tener tanto padres como hijas.

Todos los selectores son de alcance local, con lo que solo afectaran a nuestro componente y a los hijos de nuestro componente, por lo que se recomienda utilizar el patrón de alta especificidad para evitar tener colisiones.

La sangría entre instrucciones puede ser espacios o tabulaciones e incluso una combinación de ambas, solo se considera el número de espacios o tabulaciones.

Final de instrucción

Como ya lo mencioné, todas las instrucciones terminan con un salto de línea (\n o \r), a menos que sea una instrucción multi línea como puede ser el caso de los Scripts o los Atributos de selector. También se considera la instrucción terminada en el momento que aparezcan caracteres especiales de terminación.

Caracteres especiales de terminación

Ya sea por comodidad, legibilidad del código o cualquier otra situación. Algunas veces requerimos de poner diferentes instrucciones en una sola línea. Siendo este un lenguaje que termina la instrucción con saltos de línea y determina su relación entre instrucciones con la sangría, no sería posible de no ser por los caracteres de terminación: ;, :>, :<. Los cuales nos ayudan a terminar una instrucción y comenzar otra en la misma línea, manteniendo el patrón de padre-hijo-hermano, que caracteriza a este lenguaje. Estos caracteres tienen el siguiente significado:

  • ;, termina la instrucción y da por hecho que de haber una siguiente instrucción, esta será una hermana directa de la anterior.
  • :>, termina la instrucción y da por hecho que de haber una siguiente instrucción, esta será una hija directa de la anterior.
  • :<, termina la instrucción y da por hecho que de haber una siguiente instrucción, esta será una hermana de la madre actual.

ejemplo:

.padre:>display: flex;align-items: center:<.padre-2:>width: 100%

Transpila a:

.padre {
	display: flex;
	align-items: center;
}
.padre-2 {
	width: 100%;
}