How To Create Claymorphism Using CSS
A look at the new UI design trend from a developer’s point of view
Published:
Some time ago, I rewrote my blog and created a new type of content - Problems & Solutions. This type of content doesn't require images because it's more text-oriented. But of course, I needed og:image
for SEO purposes.
That's how I came to the idea that I should create my image generator!
My idea was to have five categories of solutions. That's why I created five similar images as a base for my og:image
.
For all design purposes, I use Figma. It's simple and very easy to use!
The second thing that we need is to install a node-html-to-image library.
This library is powerful, it allows us to generate images from HTML, and we were looking exactly that thing!
To install this library just type in your terminal:
npm install node-html-to-image
oryarn add node-html-to-image
Let's start coding! First of all, we need to require our library:
const nodeHtmlToImage = require('node-html-to-image');
Then let's create a basic function with an example from the library:
const nodeHtmlToImage = require('node-html-to-image');
const nodeImage = () => {
nodeHtmlToImage({
output: './image.png',
html: '<html><body>Hello world!</body></html>'
}).then(() => console.log('The image was created successfully!'));
}
nodeImage();
To run this script, just simply type node [filename].js
in your terminal and replace [filename] with your correct filename. In my case, it will be test.js
This is our log in terminal:
All good! So let's check the image:
Perfect! Our script worked, so let's create more a complex example.
Our node-image library is generating images from the HTML code, so let's add some HTML to the body:
(...)
<html>
<body>
<img src="https://albertwalicki.com/js_bg.png" alt="" width="1200" height="675"/>
<div class="wrapper">
<h1>Hello World!</h1>
<h2>This is my description!</h2>
</div>
</body>
</html>
(...)
But this isn't enough! We need to add some styles in <head>
:
(...)
<html>
<head>
<style>
html {
width: 1200px;
height: 675px;
font-family: Arial;
}
body {
width: 1200px;
height: 675px;
margin:0;
}
.wrapper {
position: absolute;
left: 60px;
top: 130px;
}
h1 {
font-size: 64px;
color: #fff;
display: block;
max-width: 1000px;
margin: 0;
line-height: 1.4;
}
h2 {
font-size: 42px;
margin: 20px 0px 0px 0;
color: rgb(183 182 181);
max-width: 1000px;
line-height: 1.3;
}
img {
width: 1200px;
height: 675px;
}
</style>
</head>
<body>(...)</body>
</html>
(...)
Our styled image looks like this:
But right now, our image is static, and there is no way to change text or base background. So, let's change this!
We don't need to use multiple different images as backgrounds to create images. We don't need even one of them. I could do it entirely in HTML and CSS. But to create five images, I spent one minute in Figma, and to do it in code, I should spend at least two minutes so that it would be a one-minute time loss 🤣.
To have reusable code, we should pass three parameters to our function:
(...)
const nodeImage = ({title, description, category}) => {
(...)
<img src={"https://albertwalicki.com/"+ category + "_bg.png"} alt="" width="1400" height="675"/>
<div class="wrapper">
<h1>${title}</h1>
<h2>${description}</h2>
</div>
(...)
});
nodeImage({title: "Hey, this is a title!", description: "A short text below title", category: 'js'});
Right now, we are using a basic font-family - Arial. But what should we do to use some fancy fonts? The easiest way is to use Google Fonts.
We just need to add some links to our <head>
like here:
(...)
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Island+Moments&display=swap" rel="stylesheet">
<style>
html {
width: 1200px;
height: 675px;
font-family: 'Island Moments', cursive;
}
</style
(...)
And there is our fancy font!
I run my image-generation scripts on build time inside Nextjs getStaticProps
. After downloading the data from the CMS, I use our nodeImage
function. That's a huge waste of resources because the function will be rendering this image every build time.
That's why I wrote another simple function:
const imageExists = async ({filename}) => {
const url = 'https://albertwalicki/solutions-img/' + filename + '.png';
const http = await fetch(url, {method: 'HEAD'})
return http.status !== 404;
}
This function fetches an image by the URL. When there's an image for a specific filename, it returns true
and false
if there is no such an image.
NOTE: You might have to install node-fetch, because the fetch API is not implemented in Node.
Link: Node Fetch github
In my case filename is the same as the URL to the solution post. All we need to do is to add this code at the very top of the nodeImage
function:
let alreadyCreated = await imageExists({filename});
if (alreadyCreated) {
console.log("exist, no need for new render")
return;
}
}
Here's a complete script for generating images:
const nodeHtmlToImage = require('node-html-to-image');
const fetch = require("node-fetch");
const imageExists = async ({filename}) => {
const url = 'https://albertwalicki.com/solutions-img/' + filename + '.png';
const http = await fetch(url, {method: 'HEAD'})
return http.status !== 404;
}
const nodeImage = async ({ title, description, category, filename }) => {
let alreadyCreated = await imageExists({filename});
if (alreadyCreated) {
console.log("exist, no need for new render")
return;
}
nodeHtmlToImage({
output: './image.png',
html: `
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Island+Moments&display=swap" rel="stylesheet">
<style>
html {
width: 1200px;
height: 675px;
font-family: 'Island Moments', cursive;
}
body {
width: 1200px;
height: 675px;
margin:0;
}
.wrapper {
position: absolute;
left: 60px;
top: 130px;
}
h1 {
font-size: 64px;
color: #fff;
display: block;
max-width: 1000px;
margin: 0;
line-height: 1.4;
}
h2 {
font-size: 42px;
margin: 20px 0px 0px 0;
color: rgb(183 182 181);
max-width: 1000px;
line-height: 1.3;
}
img {
width: 1200px;
height: 675px;
}
</style>
</head>
<body>
<img src='https://albertwalicki.com/${category}_bg.png' alt="" width="1400" height="675"/>
<div class="wrapper">
<h1>${title}</h1>
<h2>${description}</h2>
</div>
</body>
</html>`
}).then(() => console.log('The image was created successfully!'));
}
nodeImage({ title: "Extra space below image", description: "One of the most common problems that almost every junior
frontend developer had", category: 'css', filename: 'extra-space-below-image' });
And that's it! I hope you understand now how to generate images with Node!
Thank you for reading and see you next time!
Get in touch!
Have a question or need a project? Contact me!
© 2020-present Albert Walicki. All Rights Reserved
Policy代做工资流水公司湘潭制作企业对公流水台州代办银行对公流水湘潭打流水孝感薪资流水单查询深圳车贷银行流水 公司洛阳办入职银行流水开封打自存流水郑州制作转账银行流水商丘日常消费流水样本阜阳在职证明制作烟台代办入职银行流水昆明开工资银行流水南通办理薪资银行流水银川办对公账户流水银川查对公账户流水西安自存银行流水制作铜陵做对公流水阜阳自存银行流水报价鞍山对公银行流水图片信阳企业对公流水制作江门制作银行流水电子版曲靖转账银行流水图片泉州银行对公流水打印荆州银行流水公司无锡代办企业对公流水开封开企业对公流水信阳做银行流水修改赣州打印银行流水PS舟山企业对私流水价格邢台工资银行流水制作香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤