Desarrollando un Ghost CMS Theme [Parte 4]
Aquí explico sobre Content y Editor Cards en un Ghost CMS Theme.
Ivan Robles
Vamos a continuar con algunos conceptos que dejamos pendientes en el post anterior.
Contenido
Al crear una pagina o un post, podemos agregar contenido variado como es imagen, markdown, html card, galería de imágenes, bookmark, email content, botón, callout, toggle, video, audio, archivo, producto así como contenido embebido como un tweet o video de youtube.
Inicialmente lo sentí como si fuese una caja negra. Esto es porque al momento del manejo de templates lo único que vi fue {{content}}
. Los estilos dentro de content están predefinidos, si queremos hacer cambios hay que “sobre escribir” sus clases.
A continuación voy a explicar algunos elementos dentro de {{content}}
. Por cierto que decidí de ultimo momento agregar en algunos casos esos elementos en el post solo como demostración, dicho esto, no va a concordar el ejemplo de lo que es con el código (en el caso de nombres de archivos y contenido). Avisado está aquel que lea este post.
Image
En HTML van dentro de <figure>
y dentro existe un <figcaption>
.
Algo que vamos a notar es que las clases que se manejan dentro de content comienzan con el prefijo kg
.
{{/* Output */}}
<figure class="kg-image-card">
<img class="kg-image" src="<https://casper.ghost.org/v1.25.0/images/koenig-demo-1.jpg>" width="1600" height="2400" loading="lazy" srcset="..." sizes="...">
<figcaption>An example image</figcaption>
</figure>
Cuenta con helpers de tamaño de imagen con las clases kg-width-wide
y kg-width-full
:
{{/* Output */}}
<figure class="kg-image-card kg-width-wide">
<img class="kg-image" src="<https://casper.ghost.org/v1.25.0/images/koenig-demo-1.jpg>" width="1600" height="2400" loading="lazy" srcset="..." sizes="...">
</figure>
Para tamaños de imágenes responsivas se usa srcset
y sizes
:
{{/* Output */}}
<figure class="kg-card kg-image-card">
<img src="<https://myghostsite.com/content/images/2021/03/coastline.jpg>"
class="kg-image"
alt="A rugged coastline with small groups of people walking around rock pools"
loading="lazy"
width="2000"
height="3000"
srcset="<https://myghostsite.com/content/images/size/w600/2021/03/coastline.jpg> 600w,
<https://myghostsite.com/content/images/size/w1000/2021/03/coastline.jpg> 1000w,
<https://myghostsite.com/content/images/size/w1600/2021/03/coastline.jpg> 1600w,
<https://myghostsite.com/content/images/size/w2400/2021/03/coastline.jpg> 2400w"
sizes="(min-width: 720px) 720px">
</figure>
Editor Cards
Hay un montón de cards disponibles en el editor, las cuales requieren de CSS y Javascript para mostrarse y funcionar correctamente. Por default ghost nos viene con los archivos card.min.css
y cards.min.js
dentro del helper de {{ghost_head}}
.
Para hacer override (sobre escribir) de estos estilos y comportamiento para cada card individual hay que excluirlos en el package.json
:
"card_assets": {
"exclude": ["bookmark", "gallery"]
}
Gallery Cards
{{/* Output */}}
<figure class="kg-card kg-gallery-card kg-width-wide">
<div class="kg-gallery-container">
<div class="kg-gallery-row">
<div class="kg-gallery-image">
<img src="/content/images/1.jpg" width="6720" height="4480" loading="lazy" srcset="..." sizes="...">
</div>
<div class="kg-gallery-image">
<img src="/content/images/2.jpg" width="4946" height="3220" loading="lazy" srcset="..." sizes="...">
</div>
<div class="kg-gallery-image">
<img src="/content/images/3.jpg" width="5560" height="3492" loading="lazy" srcset="..." sizes="...">
</div>
</div>
<div class="kg-gallery-row">
<div class="kg-gallery-image">
<img src="/content/images/4.jpg" width="3654" height="5473" loading="lazy" srcset="..." sizes="...">
</div>
<div class="kg-gallery-image">
<img src="/content/images/5.jpg" width="4160" height="6240" loading="lazy" srcset="..." sizes="...">
</div>
<div class="kg-gallery-image">
<img src="/content/images/6.jpg" width="2645" height="3967" loading="lazy" srcset="..." sizes="...">
</div>
</div>
<div class="kg-gallery-row">
<div class="kg-gallery-image">
<img src="/content/images/7.jpg" width="3840" height="5760" loading="lazy" srcset="..." sizes="...">
</div>
<div class="kg-gallery-image">
<img src="/content/images/8.jpg" width="3456" height="5184" loading="lazy" srcset="..." sizes="...">
</div>
</div>
</div>
</figure>
Bookmark Card
{{/* Output */}}
<figure class="kg-card kg-bookmark-card">
<a href="/" class="kg-bookmark-container">
<div class="kg-bookmark-content">
<div class="kg-bookmark-title">The bookmark card</div>
<div class="kg-bookmark-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce at interdum ipsum.</div>
<div class="kg-bookmark-metadata">
<img src="/content/images/author-icon.jpg" class="kg-bookmark-icon">
<span class="kg-bookmark-author">David Darnes</span>
<span class="kg-bookmark-publisher">Ghost</span>
</div>
</div>
<div class="kg-bookmark-thumbnail">
<img src="/content/images/article-image.jpg">
</div>
</a>
</figure>
Embed Card
En caso de ser un video, hay que manejar el aspect ratio con CSS.
<figure class="kg-card kg-embed-card">
<iframe ...></iframe> <!-- <iframe> represents card content -->
</figure>
NFT Card
Estos vienen por OpenSea una de las plataformas mas famosas de NFTs (al parecer ya no se puede embeber por cosas de OpenSea... me quedé con las ganas):
<figure class="kg-card kg-embed-card kg-nft-card">
<a class="kg-nft-card"> <!-- Link to NFT on OpenSea -->
<img class="kg-nft-image"> <!-- Image of NFT -->
<div class="kg-nft-metadata">
<div class="kg-nft-header">
<h4 class="kg-nft-title"> NFT Name </h4>
</div>
<div class="kg-nft-creator">
Created by <span class="kg-nft-creator-name"> Creator Name </span>
• Collection
</div>
</div>
</a>
</figure>
Button Card
Inserta un link y el estilo se ve como un botón, su color depende de la configuración del sitio y se puede alinear a la izquierda o al centro.
<div class="kg-card kg-button-card kg-align-center">
<a href="<https://example.com/signup/>" class="kg-btn kg-btn-accent">Sign up now</a>
</div>
Callout Card
Muestra un texto resaltado con un emoji, algo parecido a un quote.
<div class="kg-card kg-callout-card kg-callout-card-accent">
<div class="kg-callout-emoji">💡</div>
<div class="kg-callout-text">Did you know about the callout card?</div>
</div>
Toggle Card
Este es un elemento que muestra contenido desplegable o colapsable.
Titulo del Toggle
Contenido del Toggle
<div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
<div class="kg-toggle-heading">
<h4 class="kg-toggle-heading-text">Do you give any discounts ?</h4>
<button class="kg-toggle-card-icon">
<svg id="Regular" xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/></svg>
</button>
</div>
<div class="kg-toggle-content">Yes, we give 20% off on annual subscriptions.</div>
</div>
Audio upload card
Pa’ los audio files.
<div class="kg-card kg-audio-card">
<img src="<https://example.com/blog/content/media/2021/12/file_example_MP3_thumb.png?v=1639412501826>" alt="audio-thumbnail" class="kg-audio-thumbnail">
<div class="kg-audio-thumbnail placeholder kg-audio-hide">
<svg width="24" height="24" fill="none" xmlns="<http://www.w3.org/2000/svg>"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 15.33a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm-2.25.75a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM15 13.83a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm-2.25.75a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0Z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M14.486 6.81A2.25 2.25 0 0 1 17.25 9v5.579a.75.75 0 0 1-1.5 0v-5.58a.75.75 0 0 0-.932-.727.755.755 0 0 1-.059.013l-4.465.744a.75.75 0 0 0-.544.72v6.33a.75.75 0 0 1-1.5 0v-6.33a2.25 2.25 0 0 1 1.763-2.194l4.473-.746Z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M3 1.5a.75.75 0 0 0-.75.75v19.5a.75.75 0 0 0 .75.75h18a.75.75 0 0 0 .75-.75V5.133a.75.75 0 0 0-.225-.535l-.002-.002-3-2.883A.75.75 0 0 0 18 1.5H3ZM1.409.659A2.25 2.25 0 0 1 3 0h15a2.25 2.25 0 0 1 1.568.637l.003.002 3 2.883a2.25 2.25 0 0 1 .679 1.61V21.75A2.25 2.25 0 0 1 21 24H3a2.25 2.25 0 0 1-2.25-2.25V2.25c0-.597.237-1.169.659-1.591Z"></path></svg>
</div>
<div class="kg-audio-player-container" style="--buffered-width:0.757576%;">
<audio src="<https://example.com/content/media/2021/12/file_example_MP3.mp3>" preload="metadata"></audio>
<div class="kg-audio-title">File example MP3</div><div class="kg-audio-player">
<button class="kg-audio-play-icon">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"></path></svg>
</button>
<button class="kg-audio-pause-icon kg-audio-hide">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"></rect><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"></rect></svg>
</button>
<span class="kg-audio-current-time">0:00</span>
<div class="kg-audio-time">
/<span class="kg-audio-duration">2:12</span>
</div>
<input type="range" class="kg-audio-seek-slider" max="132" value="0">
<button class="kg-audio-playback-rate">1×</button>
<button class="kg-audio-unmute-icon">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"></path></svg>
</button>
<button class="kg-audio-mute-icon kg-audio-hide">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"></path></svg>
</button>
<input type="range" class="kg-audio-volume-slider" max="100" value="100">
</div>
</div>
</div>
Video upload card
Pa’ los archivos de video.
<figure class="kg-card kg-video-card">
<div class="kg-video-container">
<video src="<https://example.com/video.mp4>" poster="<https://img.spacergif.org/v1/640x480/0a/spacer.png>" width="640" height="480" playsinline preload="metadata" style="background: transparent url('<https://example.com/video.png>') 50% 50% / cover no-repeat;" />
</video>
<div class="kg-video-overlay">
<button class="kg-video-large-play-icon">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg>
</button>
</div>
<div class="kg-video-player-container">
<div class="kg-video-player">
<button class="kg-video-play-icon">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg>
</button>
<button class="kg-video-pause-icon kg-video-hide">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg>
</button>
<span class="kg-video-current-time">0:00</span>
<div class="kg-video-time">/<span class="kg-video-duration"></span></div>
<input type="range" class="kg-video-seek-slider" max="100" value="0">
<button class="kg-video-playback-rate">1×</button>
<button class="kg-video-unmute-icon">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg>
</button>
<button class="kg-video-mute-icon kg-video-hide">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg>
</button>
<input type="range" class="kg-video-volume-slider" max="100" value="100">
</div>
</div>
</div>
</figure>
File upload card
Pa’ cualquier tipo de archivo.
<div class="kg-card kg-file-card ">
<a class="kg-file-card-container" href="<https://ghost.org/uploads/2017/11/file_example_PDF.pdf>" title="Download">
<div class="kg-file-card-contents">
<div class="kg-file-card-title">Sample File</div>
<div class="kg-file-card-caption">Sample file caption</div>
<div class="kg-file-card-metadata">
<div class="kg-file-card-filename">file_example_PDF.pdf</div>
<div class="kg-file-card-filesize">488 KB</div>
</div>
</div>
<div class="kg-file-card-icon">
<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 24 24"><defs><style>.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}</style></defs><title>download-circle</title><polyline class="a" points="8.25 14.25 12 18 15.75 14.25"/><line class="a" x1="12" y1="6.75" x2="12" y2="18"/><circle class="a" cx="12" cy="12" r="11.25"/></svg>
</div>
</a>
</div>
Header card
Agrega headers (aunque yo lo veo mas como un Hero) a los post y paginas.
<div class="kg-card kg-header-card kg-width-full kg-size-<size> kg-style-<style>" style="" data-kg-background-image="<https://example.com/image.jpg>">
<h2 class="kg-header-card-header">Header</h2>
<h3 class="kg-header-card-subheader">Subheader</h3>
<a href="" class="kg-header-card-button">
Button Text
</a>
</div>
También hay otros elementos como signup card, product, divider, email content, email call to action y alternative blockquote style, pero la verdad no es algo que vaya a utilizar en este blog.
Conclusión
Existen otros conceptos utilizados en el CMS de Ghost, pero no voy a entrar en detalles porque no voy a hacer uso de ellos, cual es el caso de Members, Routing y Search.
Ghost para mi es solo una alternativa al aburrido Wordpress. Y estoy seguro de que este es el único proyecto en el cual lo voy a estar utilizando, el cual es un blog sin monetizar. No miembros, no cookies, no publicidad. Solo escribir y leer.
Nos leemos pronto!! Bye!!