tech explorers, welcome!

Categoría: d.WEB

endleZZ v0.2: actualización de aniversario

Hace un año que surgió endleZZ en una sala de espera sin cobertura, como una forma de desarrollar un juego offline que no requiriese nada especial: solo un navegador y un fichero .html en tu móvil.

La idea es genial, me ha servido muchas veces para matar el tiempo, pero se merecía una mínima actualización visual y funcional.

Así que aquí está la versión 0.2: actualización de aniversario! 🎉

Versión web

Accede a la versión web oficial que hospedo en este servidor:

https://theroamingworkshop.cloud/endlezz

Versión local

O juega en local (y offline) descargando el código fuente del anterior enlace, o bien el fichero .html de github:

https://github.com/TheRoam/endleZZ

Registro de cambios

Nuevas características:

  • menú de opciones
  • controles de tiempo: pausa, restaurar y salir
  • nuevos elementos del mapa (árboles y rocas) que se generan aleatoriamente en cada partida
  • concepción inicial de sistema climático: añadida generación de nubes aleatorias

Corrección de errores:

  • las balas ahora alcanzan el final del mapa, a pesar de donde se pulse
  • ajustes del cálculo del tiempo en las pausas
  • mejoras generales de rendimiento e interacción

Three.js: Visor de modelos 3D para tu web

Estaba preparando un artículo donde quería insertar un modelo 3D para ilustrarlo mejor, y pensaba incluso en hacer un visor yo mismo. Pero no tuve que surfear mucho para encontrarme con Three.js.

https://threejs.org/

¡Si es que está ya todo inventado!

Enlazar la librería CDN

Para este ejemplo, haremos un visor "portable" enlazando las librerías al CDN oficial, en lugar de tener que descargarnos los ficheros a nuestro servidor.

De esta forma, el archivo de ejemplo te servirá en cualquier lugar con conexión a internet. Vamos a crear un fichero .html básico como el que nos sugieren en la documentación:

https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body { margin: 0; }
		</style>
	</head>
	<body>
        <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

        <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/[email protected]/build/three.module.js"
            }
          }
        </script>

        <script>
        //App code goes here
        </script>
	</body>
</html>

Crear una escena

Vamos a seguir con el ejemplo y rellenamos el segundo bloque <script> definiendo una escena con un cubo animado en rotación:

<script type="module">
        import * as THREE from 'three';
	const scene = new THREE.Scene();
	const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

	const renderer = new THREE.WebGLRenderer();
	renderer.setSize( window.innerWidth, window.innerHeight );
	document.body.appendChild( renderer.domElement );

	const geometry = new THREE.BoxGeometry( 1, 1, 1 );
	const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
	const cube = new THREE.Mesh( geometry, material );
	scene.add( cube );

	camera.position.z = 5;

	function animate() {
		requestAnimationFrame( animate );

		cube.rotation.x += 0.01;
		cube.rotation.y += 0.01;

		renderer.render( scene, camera );
	};

	animate();
</script>

Todo eso, junto, queda así:

Añade controles de arrastre y un fondo

Ahora tenemos una base para trabajar. Puede añadir más funcionalidad insertando el módulo OrbitControls que maneja la rotación del modelo y de la cámara.

//Importa nuevos módulos al principio del script
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';

//luego, añade los controles del cursor después de declarar la cámara y el renderizador
const controls = new OrbitControls( camera, renderer.domElement );

También puedes modificar el fondo fácilmente, pero necesitas hospedar la imagen junto a la aplicación en un servidor, o ejecutarlo localmente, debido al CORS. Yo usaré la imagen de la cabecera del blog, que saqué de Stellarium.

Primero, define una textura. Luego, añádela a la escena:

//añade esto antes de renderizar, mientras defines la escena
//define la textura
const texture = new THREE.TextureLoader().load( "https://theroamingworkshop.cloud/demos/Unity1-north.png" );

//añade la textura a la escena
scene.background=texture;

Código completo:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body { margin: 0; }
		</style>
	</head>
	<body>
        <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

        <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/[email protected]/build/three.module.js"
            }
          }
        </script>
<body style="margin: 0; width:100%;height:300px;">
        <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

        <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/[email protected]/build/three.module.js"
            }
          }
        </script>

    <script type="module">

    import * as THREE from 'three';
	import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';

	const scene = new THREE.Scene();
    
	const texture = new THREE.TextureLoader().load( "https://theroamingworkshop.cloud/demos/Unity1-north.png" );
	scene.background=texture;

    const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

	const renderer = new THREE.WebGLRenderer();
	renderer.setSize( window.innerWidth, window.innerHeight );
	document.body.appendChild( renderer.domElement );
    
    const controls = new OrbitControls( camera, renderer.domElement );

	const geometry = new THREE.BoxGeometry( 1, 1, 1 );
	const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
	const cube = new THREE.Mesh( geometry, material );
	scene.add( cube );

	camera.position.z = 5;

	function animate() {
		requestAnimationFrame( animate );

		cube.rotation.x += 0.01;
		cube.rotation.y += 0.01;

		renderer.render( scene, camera );
	};

	animate();
</script>
</body>
</html>

Insertar un modelo 3D

Ahora vamos a sustituir este cubo por un modelo 3D propio, que en el caso de Three.js, debe tener un formato glTF (.GLB o .GLTF), que es el formato más soportado y que renderiza más rápidamente (aunque también hay soporte para .fbx, .stl, .obj y demás).

Yo exportaré a .glb esta carcasa básica de Raspberry Pi 4B que hice hace un tiempo usando Blender:

Ahora, para insertar el modelo sustituimos el bloque <script> anterior basándonos en el ejemplo "webgl_loader_gltf" que se ve al inicio del post:

<script type="module">
import * as THREE from 'three';
import { GLTFLoader } from 'https://unpkg.com/[email protected]/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';

let camera, scene, renderer;

init();
render();

function init() {

	const container = document.createElement( 'div' );
	document.body.appendChild( container );

	camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.1, 20 );
    camera.position.set( 0.2, 0.2, 0.2 );

	scene = new THREE.Scene();        
    scene.add( new THREE.AmbientLight( 0xffffff, 0.75 ) );

	const dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
	dirLight.position.set( 5, 10, 7.5 );
	dirLight.castShadow = true;
	dirLight.shadow.camera.right = 2;
	dirLight.shadow.camera.left = - 2;
	dirLight.shadow.camera.top	= 2;
	dirLight.shadow.camera.bottom = - 2;
	dirLight.shadow.mapSize.width = 1024;
	dirLight.shadow.mapSize.height = 1024;
	scene.add( dirLight );

    //model
     const loader = new GLTFLoader();
	 loader.load( 'https://theroamingworkshop.cloud/threeJS/models/rPi4case/rPi4_case_v1.glb', function ( gltf ) {
		scene.add( gltf.scene );
		render();
	 } );

	renderer = new THREE.WebGLRenderer( { antialias: true } );
            
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.toneMapping = THREE.ACESFilmicToneMapping;
	renderer.toneMappingExposure = 1;
	renderer.outputEncoding = THREE.sRGBEncoding;
	container.appendChild( renderer.domElement );

	const controls = new OrbitControls( camera, renderer.domElement );
	controls.addEventListener( 'change', render );
    controls.minDistance = 0.001;
	controls.maxDistance = 1;
	controls.target.set( 0.03, 0.01, -0.01 );
	controls.update();
	window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( window.innerWidth, window.innerHeight );
	render();
}
function render() {
	renderer.render( scene, camera );
}
</script>

Básicamente se hace lo siguiente:

  • Importar módulos a usar:
    • GLTFLoader cargará nuestro modelo en formato .glb
    • OrbitControls nos permite controlar la vista de la cámara
  • Definir la escena:
    • definir una cámara
    • definir la luz (en este caso hay luz ambiente y direccional, prueba a comentar alguna de ellas y verás la diferencia)
  • Cargar el modelo en la escena
  • Definir los parámetros de renderizado y renderizar.

Y todo ello queda así (clicka y arrastra!):

Espero que te sea útil! Dudas o comentarios al 🐦 Twitter!

🐦 @RoamingWorkshop

😃 Usa un Emoji como favicon para tus web-apps

Visto en: https://css-tricks.com/emoji-as-a-favicon/

Toda web necesita su favicon: ese iconito que sale junto al título de la página.

Normalmente, tu favicon sería una pequeña imagen que enlazas en el encabezado del .html, como en este blog (aunque aquí lo hace WordPress por mí).

<link rel="icon" href="https://theroamingworkshop.cloud/b/wp-content/uploads/2022/08/cropped-TRW-favicon-1-32x32.png" sizes="32x32">

Pero para tus webapps de uso personal no es necesario perder el tiempo diseñando una imagen, ni tampoco dejar el cutre icono por defecto del navegador.

Puedes usar un Emoji, que le dará un toque más personal a tu página, tal y como nos indica Lea Verou en este tweet:

Resumiendo

Vamos, que todo lo que tienes que hacer es copiar y pegar lo siguiente:

  1. Pega este código dentro de tu <html>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>">
  1. Sustituye el icono «⚡» por cualquiera de los símbolos HTML, como los que encontrarás en w3schools (selecciónalo y cópialo):

https://www.w3schools.com/charsets/ref_emoji.asp

  1. En caso de que veas símbolos raros en lugar del icono, añade codificación UTF-8 a tu documento:
    <meta charset="UTF-8">

Código ejemplo

Aquí te dejo un código de ejemplo para que lo pruebes en un archivo .html en tu ordenador.

<html>

<meta charset="UTF-8">

<title>Mi web app</title>

<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>">

</html>

Consulta y comparte este y más trucos de diseño web en 🐦 Twitter!

🐦 @RoamingWorkshop

Comprueba el rastreo que hacen las webs que visitas.

Si quieres comprobar el seguimiento que hace de tí una web haz este sencillo experimento.

  1. Entra en el inspector del navegador con Crtl+I o click derecho «Inspeccionar»
  2. Abre la pestaña de «Red».
  3. Ahora ve a la página principal del blog.

Esto es lo que verás: apenas 8 archivos que cargan todo lo necesario para que navegues por el blog.

Ahora abre cualquier otra página que suelas visitar y observa la misma pestaña de red.

Yo voy a entrar en amazon.es y no voy a entrar en detalles. Solo fíjate que se irá generando un nuevo fichero /1/batch/1/OE al final cada poco tiempo. Ahora mueve despacio el ratón sin bajar la ventana. Verás que el contenido de esos ficheros contiene una variable t de tiempo y unas coordenadas x e y que indican donde tienes el cursor en cada momento. Esto es solo un ejemplo del tracking que se realiza habitualmente: desde dónde entraste a la web, qué clickas, qué ves, donde te detienes, tu IP, tu geolocalización… Aquí no hay nada de eso.

Esto en cuanto a Amazon y su AWS. Los ficheros de rastreo de Google son los famosos «analytics» o «collects» que también verás en la pestaña de Red en las páginas que lo utilicen (que son la mayoría).

Se suele decir que la mejor solución es la más sencilla, y de hecho, como ves en este blog, no necesitas tantas florituras para publicar una web. Así que desconfía de sitios tediosos, con un interminable listado de «partners» y con servicios de Google Analytics o Amazon Web Services.

Cuida tu navegación!

Editor de texto interactivo para tus web apps con Rich Text Editor

Uno de mis últimos proyectos es una app de notas para alojarlas en mi servidor y prescindir de [G] Notes.

La característica más interesante que incluye es Rich Text Editor, una completa librería javascript en forma de interfaz para dar formato HTML al texto de cualquier formulario web.

Es de código abierto, gratis para uso personal, y muy simple de usar. Todo un win-win que te traigo hoy.

Licencia

He dicho que Rich Text Editor es gratis, y es así si le das un uso no comercial.

Es importante que leas su página de precios para que entiendas las limitaciones de la licencia gratuita:

  • 1 desarrollador
  • 1 dominio
  • 5 usuarios activos

En mi caso, he instalado Rich Text Editor en este servidor que aloja el blog, y lo he restringido mediante php para que solo se pueda acceder desde el propio dominio.

TIP! No hospedes los archivos en cualquier sitio que sea accesible por cualquiera (como un servidor público), ya que podrías dar pie a un incumplimiento de licencia, teniendo que pagar una licencia standard, o dando de baja tu app.

Aun así, las licencias comerciales son de un único pago y puede que te merezca la pena obtener una si vas a darle más uso.

Instalación

Usar Rich Text Editor es muy sencillo, y su página de documentación lo explica a la perfección.

Para instalar Rich Text Editor solo tenemos que descargar los ficheros de su página web a una carpeta local o a una ubicación de tu servidor.

https://richtexteditor.com/download.aspx

Habilitar interfaz

Rich Text Editor funciona dentro de un documento HTML.

Para habilitarlo:

  1. Como siempre, creamos un fichero HTML con su estructura básica.
  2. Enlazamos los scripts y el CSS en el <head> :

    <link rel="stylesheet" href="/richtexteditor/rte_theme_default.css" />  
    <script type="text/javascript" src="/richtexteditor/rte.js"></script>  
    <script type="text/javascript" src='/richtexteditor/plugins/all_plugins.js'></script>  

  3. Dentro del <body> añadimos el siguiente <div> :

    <div id="div_editor1" > 
    <p>Initial Document Content</p> 
    </div>

  4.   Por último, llamamos al editor en el <script> :

    var editor1 = new RichTextEditor("#div_editor1", {skin:"rounded-corner", toolbar:"full"});

Todo lo anterior se vería así (puedes interactuar con él):

Y aquí el código completo:

<html>
<head>

<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css" />
<script type="text/javascript" src="/richtexteditor/rte.js"></script>
<script type="text/javascript" src='/richtexteditor/plugins/all_plugins.js'></script

</head>
<body>

<div id="div_editor1" > 
<p>Initial Document Content</p> 
</div> 

</body>
<script>

var editor1 = new RichTextEditor("#div_editor1", {skin:"rounded-corner", toolbar:"full"});

</script>
</html>

TIP! Copia y pega el código anterior en un fichero .html, dentro de la carpeta de richtexteditor, y podrás ver este ejemplo en tu navegador.

Configuración

Como verás, he añadido algunas propiedades a la hora de llamar al editor:

{skin:"rounded-corner", toolbar:"full"}

Hay múltiples opciones de configuración que puedes consultar aquí:

https://richtexteditor.com/docs/configuration-reference.aspx

Funciones del editor

La librería contiene múltiples funciones en forma de API para interactuar con el editor. Todos los comandos que se pueden usar se encuentran aquí:

https://richtexteditor.com/docs/cmd_allcommands.aspx

Una funcionalidad básica es capturar el texto que hemos ido editando en el cuadro de texto. Es lo que hace el botón de la parte inferior, usando la función getHTMLcode():

<button onclick="alert(editor1.getHTMLCode())">Show Html Code</button>

Usos

Como comenté anteriormente, el uso que le voy a dar a esta librería es en un editor de notas que se alojará en mi servidor.

El editor de texto me sirve para dar formato a las notas muy fácilmente y guardarlas con todo el formato.

Aquí tienes una demo de cómo va quedando la interfaz, aunque no tiene las funciones habilitadas todavía.

Otras aplicaciones en las que puedes usar este editor es en foros, formularios, o cualquier cuadro de texto que tenga que rellenar un usuario de tu web app.

Espero que te resulte tan útil como a mí y que le des buen uso. Si tienes dudas o comentarios puedes enviarlos por Twitter 🐦 Hasta pronto!

🐦 @RoamingWorkshop

Widgets personalizados para tu sitio WordPress

Para prescindir de plugins externos innecesarios, aquí verás cómo hacer tus propios widgets para insertar en tu página de WordPress o dentro de cualquier post.

Sólo tienes que saber algo de HTML y de javascript. Vamos a ello!

Custom HTML

Si has visto algún otro post de este blog ya habrás visto la tabla de contenido de arriba, o la notificación de cookies en la barra lateral.

Ambos son elementos dinámicos que he programado con HTML y javascript y que puedes insertar en múltiples sitios de tu WordPress usando un bloque "Custom HTML".

Simplemente inserta este bloque y dentro podrás introducir tu código HTML personalizado y programable con javascript. Veamos unos ejemplos.

Notificación de cookies

La barra lateral de este blog muestra un mensaje de consentimiento de cookies, con un enlace a la política de privacidad.

Puede que hayas visto que al cambiar el idioma de la página, se cambia el contenido de dicho mensaje automáticamente.

Haz click derecho para inspeccionar la estructura de la página.

Para que funcione correctamente, imagina que el bloque Custom HTML que insertas es un iframe o una especie de página insertada; una sub-página dentro de nuestra página.

Editamos el contenido del Custom HTML como si de otra página se tratara, y podemos añadir libremente lo que queramos.

Para la notificación, solo quiero una línea de texto, a la que asigno la id "cookie-txt". Es bueno añadir toda la estructura HTML básica, aunque se puede prescindir de muchos elementos.

<html>
<body style="background-color:'lightgrey';width:100%;">
<p id="cookie-txt"></p>
</body>
</html>

Ahora añado una función javascript que lea el idioma de la página y rellene esta línea en consecuencia.

Puedes hacer distintas pruebas usando la consola y el inspector del navegador, haciendo click derecho en la página.

Una forma rápida de obtener el idioma es leyendo la propiedad lang="es-ES" del elemento <html> de la página (o lang="en-GB" para inglés).

El resto es una condición:

  • Si se detecta "es-ES", definimos el mensaje en Español.
  • Si se detecta "en-GB", definimos el mensaje en Inglés.

El contenido del mensaje lo definimos mediante la propiedad innerHTML del elemento <p>, por lo que podemos darle cualquier formato HTML, como incrustarle un enlace o resaltar en negrita.

<script>
var lang=document.getElementsByTagName("html")[0].lang;
if(lang=="es-ES"){
document.getElementById("cookie-txt").innerHTML="🍪 <a href='https://theroamingworkshop.cloud/b/?page_id=1225' target='_blank'>Política</a> <b>anti-Cookies</b> aceptada al navegar.</p>";
}else if(lang=="en-GB"){
document.getElementById("cookie-txt").innerHTML="<b>🍪 anti-Cookies</b> <a href='https://theroamingworkshop.cloud/b/?page_id=3' target='_blank'>policy</a> accepted while browsing.";
}
</script>

Por lo que quedaría algo así:

El código completo del Custom HTML que muestra la notificación de cookies es este:

<html>
<body style="background-color:'lightgrey';width:100%;">
<p id="cookie-txt"></p>
</body>
<script>
var lang=document.getElementsByTagName("html")[0].lang;
if(lang=="es-ES"){
document.getElementById("cookie-txt").innerHTML="🍪 <a href='https://theroamingworkshop.cloud/b/?page_id=1225' target='_blank'>Política</a> <b>anti-Cookies</b> aceptada al navegar.</p>";
}else if(lang=="en-GB"){
document.getElementById("cookie-txt").innerHTML="<b>🍪 anti-Cookies</b> <a href='https://theroamingworkshop.cloud/b/?page_id=3' target='_blank'>policy</a> accepted while browsing.";
}
</script>
</html>

El bloque Custom HTML, en este caso, lo inserto como un elemento de la barra lateral:

Tabla de contenido

La tabla de contenido que aparece en cada post viene a ser más o menos lo mismo que lo anterior.

En primer lugar, defino la tabla dándole formato, y dejando el contenido vacío para rellenar luego con una función.

<div id="menu" style="padding:20px 20px 20px 20px; border-left:2px solid darkgrey;">
<p style="font-weight:bold;">Contenido</p>
</div>

En este caso, lo que haré es buscar los "headings" o títulos de cada apartado, en concreto los de etiqueta <h2>. Por ello, al redactar los posts debo usar siempre este tipo de encabezado (que es el habitual) si quiero que aparezca en la tabla.

Además, defino un ancla o html Anchor, que se asignará como "id", lo cual nos permitirá crear un enlace que nos lleve hasta dicho encabezado.

La función hará lo siguiente:

  1. Buscar los elementos <h2> uno por uno.
    for (let i=0;i < window.document.getElementsByTagName("h2").length; i++) { element = window.document.getElementsByTagName("h2")[i];
    ...
  2. Tomar el texto que contiene.
    text = "▹ "+element.innerHTML;
  3. Crear el nuevo elemento de tipo hiperenlace (<a>) que irá a la tabla de contenido, con el texto anterior.
    var newelement = document.createElement("a"); newelement.innerHTML=text;
  4. Tomar la "id" del post y del encabezado para crear un enlace a dicho encabezado.
    var postid = window.document.getElementsByTagName("article")[0].id;
    var url = "https://theroamingworkshop.cloud/b/?p="+postid.substr(5,postid.length)+"#"+element.id;
  5. Añadirlo todo en una línea al elemento de la tabla de contenido
    newelement.setAttribute("href", url); newelement.appendChild(document.createElement("br")); window.document.getElementById("menu").appendChild(newelement);

TIP! Añadiendo #id-de-la-sección al final de una URL te dirije a dicho apartado.

  1. Por último, algunas páginas tardan un tiempo en cargar y no están todos los encabezados disponibles al inicio, por lo que meto todo el código anterior en una función, y la invoco con un tiempo de retardo.
    window.setTimeout(fillmenu,500);

El código del bloque completo sería el siguiente:

<div id="menu" style="padding:20px 20px 20px 20px; border-left:2px solid darkgrey;">
<p style="font-weight:bold;">Contenido</p>
</div>
<script>
var text;
var element;

function fillmenu(){

for (let i=0; i<window.document.getElementsByTagName("h2").length; i++){
element = window.document.getElementsByTagName("h2")[i];
text = "▹ "+element.innerHTML;
var newelement = document.createElement("a");
newelement.innerHTML=text;
var postid=window.document.getElementsByTagName("article")[0].id;
var url = "https://theroamingworkshop.cloud/b/?p="+postid.substr(5,postid.length)+"#"+element.id;
newelement.setAttribute("href", url);
newelement.appendChild(document.createElement("br"));
window.document.getElementById("menu").appendChild(newelement);
}
}

window.setTimeout(fillmenu,500);

</script>

TIP! Añade este bloque HTML a tus bloques reusables y lo podrás insertar en todos tus posts. Este bloque de TIP! es otro ejemplo.

Bloque de TIP!

TIP! Este bloque de TIP! es otro ejemplo. Tendrás que reconvertir a un bloque regular si quieres alterar el contenido solo en el bloque insertado

Aquí su código:

<p style="border-left:3px solid orange;padding-left:5px;font-size:14px;"><i><b>TIP! </b>Este bloque de <b>TIP!</b> es otro ejemplo. Tendrás que reconvertir a un bloque regular si quieres alterar el contenido <b>solo</b> en el bloque insertado</i></p>

Conclusión

Como verás, las posibilidades son realmente ilimitadas así que queda todo a expensas de tu imaginación.

Se creativo y diseña tus propios widgets, ya verás lo bien complementan tu blog!

Como siempre, dudas o comentarios al Twitter 🐦 ¡Hasta otra!

(el bloque de Twitter también es un reutilizable 👀 )

🐦 @RoamingWorkshop