-
-
Save mithicher/de63e925c70b90ba2d6b4eea5cc43e03 to your computer and use it in GitHub Desktop.
Simple Open Graph Image Generator with AlpineJS and Tailwind CSS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script defer src="https://unpkg.com/alpinejs@3.9.0/dist/cdn.min.js"></script> | |
<style> | |
.pattern-grid { | |
background-image: linear-gradient(currentColor 1px, transparent 1px), linear-gradient(to right, currentColor 1px, transparent 1px); | |
background-size: 40px 40px; | |
} | |
[x-cloak] { display: none; } | |
</style> | |
</head> | |
<body class="bg-slate-100"> | |
<div class="relative flex items-top justify-center min-h-screen bg-gray-50 dark:bg-gray-900 sm:items-center py-4 sm:pt-0" | |
x-data="{ | |
endpoint: 'https://www.googleapis.com/webfonts/v1/webfonts?key=AIzaSyBTZIBr5YQccTE1nPuWvBVIEbdExyzdCrY', | |
fonts: [], | |
fontName: 'Space Mono', | |
alignment: 'center', | |
theme: 'theme1', | |
title: 'Simple Open Graph Image Generator with AlpineJS and Tailwind CSS', | |
author: 'Written by @mithicher', | |
getFontsLists() { | |
fetch(this.endpoint) | |
.then(response => response.json()) | |
.then(response => { | |
this.fonts = response.items.map((item) => item.family) | |
}) | |
}, | |
generateFontUrl() { | |
return `https://fonts.googleapis.com/css2?family=${this.fontName.replace(' ', '+')}&display=swap`; | |
}, | |
generateImage() { | |
htmlToImage.toPng(document.getElementById('banner')) | |
.then(function (dataUrl) { | |
const link = document.createElement('a') | |
link.download = 'og-banner.png' | |
link.href = dataUrl | |
link.click() | |
}) | |
.catch(function (error) { | |
console.error('oops, something went wrong!', error); | |
}); | |
}, | |
alignText(value) { | |
this.alignment = value | |
} | |
}" x-init="getFontsLists" x-cloak> | |
<link :href="generateFontUrl" rel="stylesheet" crossorigin="anonymous"> | |
<!-- Settings Toolbar --> | |
<div class="fixed top-0 left-0 w-full bg-white flex items-center h-16 shadow"> | |
<div class="max-w-[75rem] mx-auto flex-1"> | |
<div class="flex space-x-3"> | |
<select x-model="theme" class="transition duration-150 ease-in-out px-3 h-10 py-2 block text-gray-700 font-sans rounded-lg text-left focus:outline-none focus:border-indigo-500 focus:ring-indigo-500 shadow-sm border sm:text-sm placeholder-gray-400 bg-white disabled:bg-gray-100 border-gray-300"> | |
<option value="" disabled>Select a theme</option> | |
<option value="theme1" :selected="theme === 'theme1'" x-text="'Theme 1'"></option> | |
<option value="theme2" :selected="theme === 'theme2'" x-text="'Theme 2'"></option> | |
<option value="theme3" :selected="theme === 'theme3'" x-text="'Theme 3'"></option> | |
</select> | |
<select x-model="fontName" class="transition duration-150 ease-in-out px-3 h-10 py-2 block text-gray-700 font-sans rounded-lg text-left focus:outline-none focus:border-indigo-500 focus:ring-indigo-500 shadow-sm border sm:text-sm placeholder-gray-400 bg-white disabled:bg-gray-100 border-gray-300"> | |
<option value="" disabled>Select a font</option> | |
<template x-for="(font, fontIndex) in fonts" :key="fontIndex"> | |
<option :value="font" :selected="fontName === font" x-text="font"></option> | |
</template> | |
</select> | |
<div class="flex items-center shadow-sm border border-gray-300 bg-white rounded-lg px-1 space-x-1.5"> | |
<button class="block w-8 h-8 hover:bg-indigo-100 rounded-lg" :class="{'bg-indigo-100': alignment === 'center'}" type="button" x-on:click="alignText('center')"><svg | |
xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 mx-auto" fill="#6366f1" viewBox="0 0 256 256"> | |
<rect width="256" height="256" fill="none"></rect> | |
<line x1="40" y1="68" x2="216" y2="68" fill="none" stroke="#6366f1" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="16"></line> | |
<line x1="64" y1="108" x2="192" y2="108" fill="none" stroke="#6366f1" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="16"></line> | |
<line x1="40" y1="148" x2="216" y2="148" fill="none" stroke="#6366f1" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="16"></line> | |
<line x1="64" y1="188" x2="192" y2="188" fill="none" stroke="#6366f1" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="16"></line> | |
</svg></button> | |
<div class="h-6 border-l"></div> | |
<button class="block w-8 h-8 hover:bg-indigo-100 rounded-lg" :class="{'bg-indigo-100': alignment === 'left'}" type="button" x-on:click="alignText('left')"><svg | |
xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 mx-auto" fill="#6366f1" viewBox="0 0 256 256"> | |
<rect width="256" height="256" fill="none"></rect> | |
<line x1="40" y1="68" x2="216" y2="68" fill="none" stroke="#6366f1" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="16"></line> | |
<line x1="40" y1="108" x2="168" y2="108" fill="none" stroke="#6366f1" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="16"></line> | |
<line x1="40" y1="148" x2="216" y2="148" fill="none" stroke="#6366f1" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="16"></line> | |
<line x1="40" y1="188" x2="168" y2="188" fill="none" stroke="#6366f1" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="16"></line> | |
</svg></button> | |
</div> | |
<div class="flex items-center shadow-sm border border-gray-300 bg-white rounded-lg px-2 space-x-2"> | |
<button class="flex text-gray-600 pr-2" type="button" x-on:click="generateImage()"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 mr-1" fill="#6366f1" viewBox="0 0 256 256"> | |
<rect width="256" height="256" fill="none"></rect> | |
<path | |
d="M40,176V48a8,8,0,0,1,8-8H208a8,8,0,0,1,8,8V160h0l-42.3-42.3a8,8,0,0,0-11.4,0l-44.6,44.6a8,8,0,0,1-11.4,0L85.7,141.7a8,8,0,0,0-11.4,0Z" | |
opacity="0.2"></path> | |
<rect x="40" y="40" width="176" height="176" rx="8" fill="none" stroke="#6366f1" | |
stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></rect> | |
<path | |
d="M216,160l-42.3-42.3a8,8,0,0,0-11.4,0l-44.6,44.6a8,8,0,0,1-11.4,0L85.7,141.7a8,8,0,0,0-11.4,0L40,176" | |
fill="none" stroke="#6366f1" stroke-linecap="round" stroke-linejoin="round" | |
stroke-width="16"></path> | |
<circle cx="100" cy="92" r="12"></circle> | |
</svg>Download image | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- ./Settings Toolbar --> | |
<div class="max-w-[75rem] mx-auto flex-1"> | |
<div | |
:style="`font-family: ${fontName}, san-serif`" | |
class="overflow-hidden shadow" | |
:class="{ 'text-center': `${alignment}` === 'center', 'text-left': `${alignment}` === 'left'}"> | |
<div id="banner" style="height: 640px" class="relative flex flex-col shadow bg-white max-w-full"> | |
<!-- Banner Style with Tailwind CSS: Theme 1 --> | |
<div x-show="theme === 'theme1'" class="flex-1 flex items-center border-[1em] border-cyan-600 bg-gray-900"> | |
<div class="relative z-10 px-40 py-6 flex-1"> | |
<h2 class="font-semibold text-slate-200 text-6xl leading-tight" contenteditable="true" spellcheck="false" x-text="title"></h2> | |
<p class="text-slate-400 font-mono text-xl mt-8" contenteditable="true" x-text="author"></p> | |
</div> | |
<div class="w-64 absolute right-3 top-3"> | |
<div class="grid grid-cols-6"> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
</div> | |
</div> | |
<div class="w-64 transform -rotate-180 absolute left-3 bottom-3"> | |
<div class="grid grid-cols-6"> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10"></div> | |
<div class="h-10 bg-cyan-600"></div> | |
</div> | |
</div> | |
</div> | |
<!-- ./Banner Style with Tailwind CSS: Theme 1 --> | |
<!-- Banner Style with Tailwind CSS: Theme 2 --> | |
<div x-show="theme === 'theme2'" class="relative flex-1 flex items-center bg-slate-50"> | |
<div class="-skew-y-12 w-full -mt-12 absolute top-0"> | |
<div class="grid grid-cols-6"> | |
<div class="h-12 col-span-1 bg-slate-200"></div> | |
<div class="h-12 col-span-5 bg-white"></div> | |
</div> | |
<div class="grid grid-cols-4"> | |
<div class="h-12 col-span-1 bg-slate-100"></div> | |
<div class="h-12 col-span-2 bg-white"></div> | |
<div class="h-12 col-span-1 bg-gray-100"></div> | |
</div> | |
<div class="grid grid-cols-12"> | |
<div class="h-12 col-span-1 bg-indigo-600"></div> | |
<div class="h-12 col-span-3 bg-indigo-300"></div> | |
<div class="h-12 col-span-8 bg-white"></div> | |
</div> | |
<div class="grid grid-cols-8"> | |
<div class="h-12 col-span-6 bg-white"></div> | |
<div class="h-12 col-span-2 bg-indigo-600"></div> | |
</div> | |
<div class="grid grid-cols-8"> | |
<div class="h-12 col-span-7 bg-white"></div> | |
<div class="h-12 col-span-1 bg-indigo-300"></div> | |
</div> | |
</div> | |
<div class="relative z-40 px-40 py-6"> | |
<h2 class="font-semibold text-slate-800 text-6xl leading-tight" contenteditable="true" spellcheck="false" x-text="title"></h2> | |
<p class="text-slate-600 font-mono text-xl mt-8" contenteditable="true" x-text="author"></p> | |
</div> | |
<div class="-skew-y-12 w-full -mb-20 absolute bottom-0"> | |
<div class="grid grid-cols-5"> | |
<div class="h-12 col-span-1 bg-indigo-600"></div> | |
<div class="h-12 col-span-4 bg-white"></div> | |
</div> | |
<div class="grid grid-cols-4"> | |
<div class="h-12 col-span-1 bg-slate-100"></div> | |
<div class="h-12 col-span-2 bg-white"></div> | |
<div class="h-12 col-span-1 bg-slate-100"></div> | |
</div> | |
<div class="grid grid-cols-6"> | |
<div class="h-12 col-span-1 bg-indigo-600"></div> | |
<div class="h-12 col-span-1 bg-indigo-300"></div> | |
<div class="h-12 col-span-4 bg-white"></div> | |
</div> | |
<div class="grid grid-cols-8"> | |
<div class="h-12 col-span-6 bg-white"></div> | |
<div class="h-12 col-span-2 bg-indigo-600"></div> | |
</div> | |
<div class="grid grid-cols-8"> | |
<div class="h-12 col-span-7 bg-white"></div> | |
<div class="h-12 col-span-1 bg-indigo-300"></div> | |
</div> | |
</div> | |
</div> | |
<!-- ./Banner Style with Tailwind CSS: Theme 2 --> | |
<!-- Banner Style with Tailwind CSS: Theme 3 --> | |
<div x-show="theme === 'theme3'" class="flex-1 flex items-center pattern-grid text-pink-100"> | |
<div class="h-96 bg-gradient-to-b from-pink-100 via-purple-50 absolute top-0 left-0 right-0"></div> | |
<div class="h-96 bg-gradient-to-t from-white absolute bottom-0 left-0 right-0"></div> | |
<div class="relative z-10 px-40 py-6"> | |
<h2 class="font-semibold text-pink-800 text-6xl leading-tight" contenteditable="true" spellcheck="false" x-text="title"></h2> | |
<p class="text-slate-600 font-mono text-xl mt-16" contenteditable="true" x-text="author"></p> | |
</div> | |
</div> | |
<!-- ./Banner Style with Tailwind CSS: Theme 3 --> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script src="https://cdn.jsdelivr.net/npm/html-to-image@1.9.0/dist/html-to-image.js" crossorigin="anonymous"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Preview: