This article has been modified in some places, check the latest version of the article here.
Long article warning (I accidentally wrote over ten thousand words... I really wish I had this motivation when writing papers). These past few days, perhaps to escape some real-life pressures, I have been tinkering like crazy. I set up a new blog using nobelium, played around with Tailwind CSS, and bought a domain name, giving my blog its own domain.
Actually, I discovered the nobelium project last year and successfully set it up, but I always wanted to adjust the layout style of the website. However, I didn't know how to do it and didn't spend time tinkering at that time. These past few days, I focused on learning and tinkering.
Deployment Process#
The setup process for nobelium is clearly described in the README, so I will briefly summarize it here. First, you need to prepare a GitHub account, a Notion account, and a Vercel account (you can log in directly with GitHub), and fork the craigary/nobelium repository.
Then duplicate the Notion template provided by craigary (a database) to your own Notion account and share this database in your Notion.
After sharing, you need to record your Page ID, as each database is different, and craigary's image explains this very clearly.
Next, log in to Vercel, choose to create a new project, import the repository you just forked from GitHub, the operations are quite intuitive, so I won't include screenshots here, and then select the Next.js framework. After that, set the environment variable with the name NOTION_PAGE_ID
and the content as your Page ID to let Vercel deploy automatically.
Once the deployment is successful, visit the URL provided by Vercel, and you should be able to see the content. At this point, you can take a look at that template database, and the meaning of each attribute inside should be clear.
- title: The title of the article;
- date: The time the article was published; if left blank, it defaults to the article creation time;
- slug: The characters at the end of the article URL, which is the part after
.com/xxx
, it is recommended to use English and replace spaces with-
; if using Chinese or spaces, punctuation, it will be UrlEncoded into a long string, making the link look very long; - summary: A brief introduction to the article, which will be displayed at the bottom of the homepage title;
- tags: Article tags, which can categorize the article;
- status: The status of the article; only articles with the
Published
status will be publicly displayed on the blog; - type:
Post
will be displayed directly on the homepage, whilePage
will only be displayed when the URL is accessed directly and will not appear in the homepage article list.
I personally used Notion's Group feature to group this database by status, so I can manage published and unpublished content more conveniently.
After that, I found a few things I had written before, pasted them into the Notion template, and then returned to the blog. Just refreshing the page allowed me to see the articles.
Start Tinkering#
blog.config#
Clone the repository you forked to your local machine, and you can start tinkering.
According to the introduction in the README, you can modify the blog.config.js
file for the first customization of the blog. The customizable content is also well-commented in the file, so do not modify the parts marked as do not change by the author. Here are two points I think need attention.
- First, remember what you filled in for
email
, as it is related to the avatar, which will be explained in detail later; - Secondly, for
comment
, I personally chose Cusdis instead of Gitalk, which will also be explained in detail later.
Avatar: Gravatar#
By default, the avatar in nobelium looks like this:
Very familiar, as this is Gravatar's default avatar. You need to register an account on the Gravatar website (a WordPress account is also fine), and be sure to register with the email you filled in earlier in email
, then follow the prompts to upload your avatar. After successfully uploading, wait a while, and your avatar should refresh on the blog.
The principle is that nobelium will automatically use the avatar URL generated by Gravatar to fetch the avatar; you just need to add the md5 value of the registered email after https://gravatar.com/avatar/
to directly obtain the avatar, which can be seen in layout.js
Ln45.
Comments: Cusdis#
The comment system I used in Gridea was Gitalk, which, when bound to the repository, allowed comments through issues and supported Markdown. Anyone with a GitHub account could comment. However, the experience was a bit heavy, and in the domestic internet environment, it often failed to load. Some people also believe this is a misuse of GitHub's issue feature. But for me, the most frustrating part was that customizing styles was quite difficult.
So when I discovered Cusdis in nobelium, I decided to give it a try, and the result was pleasantly surprising. According to the official introduction, Cusdis is a lightweight open-source alternative to Disqus, only 5kb in size. After successful setup, it loads in sync with the webpage and does not require any account registration to comment.
Cusdis can be self-hosted or use the official server. You just need to register an account on the official website, add a website, remember the data-app-id
, and fill it in the corresponding position in blog.config.js
. If using the official server, you can fill it in like this:
cusdisConfig: {
appId: 'replace with your data-app-id', // data-app-id
host: 'https://cusdis.com', // data-host, change this if you're using self-hosted version
scriptSrc: 'https://cusdis.com/js/cusdis.es.js' // change this if you're using self-hosted version
}
After completing this, you can push all the changes to GitHub, wait for Vercel to finish deploying, and then refresh to see the results!
Tailwind CSS#
The above is the most basic customization, but for someone like me who loves tinkering, it definitely won't be enough; I still enjoy messing with CSS.
On the first day after setting up nobelium, I actually tinkered with the styles a bit, but since I didn't read the documentation carefully, I directly modified the more obvious global.css
and notion.css
files in the repository. I didn't understand the content inside, so I just used the inspection tool to hardcode some styles I wanted using CSS and barbarically added !important
to force them to take effect. However, the next day, after carefully reviewing the nobelium documentation, I found that nobelium also mentioned that it uses the Tailwind CSS / JIT framework for easy customization, so I looked up this familiar name and realized I had missed out on too much. My previous customizations were basically crude and not elegant at all.
To put it simply, the two CSS files in nobelium are responsible for two parts: global.css
mainly handles the overall framework, including the navigation bar, homepage, footer, article authors, etc.; notion.css
mainly handles the article body. You can understand that nobelium provides a display framework for the content in Notion, while the other content outside the body is generated by nobelium, and the body content is fetched from Notion and rendered with specified styles, ultimately presenting a complete blog.
Returning to Tailwind CSS, based on my understanding, Tailwind CSS provides a framework for quickly specifying styles in HTML files. You only need to specify some specific classes in the HTML elements, and it will automatically generate the corresponding CSS properties, achieving the effect of quickly customizing styles without leaving the HTML file. Therefore, looking back at the two CSS files, the content inside seems understandable.
For example, in the notion.css
file, I customized the following:
.notion-h1, .notion-h2, .notion-h3 {
@apply mb-2;
@apply text-left;
}
Since Tailwind is used in CSS, the rules for selectors are the same as CSS, and the content after @apply
is the "syntax" of Tailwind CSS. mb-2
represents margin-bottom: 0.5rem; /* 8px */
, and text-left
represents text-align: left;
.
However, not all styles are specified in these two CSS files. As mentioned earlier, the main function of Tailwind CSS is to quickly specify styles in HTML files, so we can also find traces of Tailwind CSS in several JS files that generate HTML files.
For example, I modified BlogPost.js
to look like this:
const BlogPost = ({ post }) => {
return (
<Link href={`${BLOG.path}/${post.slug}`}>
<a>
<article key={post.id} className="mb-6 md:mb-8">
<header className="flex flex-col justify-between md:flex-row md:items-baseline">
<h2 className="text-lg md:text-xl font-bold mb-2 cursor-pointer text-black dark:text-gray-200">
{post.title}
</h2>
<time className="flex-shrink-0 text-gray-600 dark:text-gray-500">
{formatDate(post?.date?.start_date || post.createdTime, BLOG.lang)}
</time>
</header>
<main>
<p className="hidden md:block leading-7 text-gray-700 dark:text-gray-400">
{post.summary}
</p>
</main>
</article>
</a>
</Link>
)
}
This file is actually the display effect of the homepage. I wanted to darken the summary text a bit and make the article title text stand out more, so I modified the className
of the last <p>
element to text-gray-700
and dark:text-gray-400
. According to the documentation, text-color-###
represents the font color of the specified color, where gray-700
is a shade of gray from Tailwind CSS's default palette, and the dark:
prefix indicates it applies in dark mode, meaning the text color in dark mode uses the gray-400
color, which is darker than the original, achieving my desired effect.
Similarly, detailed correspondence can be found in the Tailwind CSS official documentation, which has a convenient search function. As a pure amateur, I found the tinkering experience quite enjoyable. Additionally, there is a Chinese translation of the documentation available for reference.
After that, I can further modify some elements, such as adding the CC BY-NC-SA icon to the footer and hyperlinking it to the blog's copyright page, while removing the original Vercel badge (which I moved to the about page).
Notion Colors in Dark Mode#
By default, since my background color is set to pure black, the default palette displays Notion colors (including but not limited to font colors, highlight colors, background colors, etc.) very faintly. Therefore, I manually extracted various colors from Notion's official dark mode and wrote them into notion.css
.
These colors should be usable under various dark backgrounds with 0 saturation (Notion's official dark background color is #191919
, if you need it, you can add the following content anywhere in the notion.css
file:
@media (prefers-color-scheme:dark) {
:root {
--fg-color-0: #6b728080;
--notion-red: #DF5452;
--notion-pink: #D15796;
--notion-blue: #5E87C9;
--notion-purple: #9D68D3;
--notion-teal: #529E72;
--notion-yellow: #CA9849;
--notion-orange: #C77D48;
--notion-brown: #BA856F;
--notion-gray: #9B9B9B;
--notion-red_background: #522E2A;
--notion-pink_background: #4E2C3C;
--notion-blue_background: #143A4E;
--notion-purple_background: #3C2D49;
--notion-teal_background: #243D30;
--notion-yellow_background: #564328;
--notion-orange_background: #5C3B23;
--notion-brown_background: #4A3228;
--notion-gray_background: #2F2F2F;
--notion-red_background_co: #362422;
--notion-pink_background_co: #302228;
--notion-blue_background_co: #1D282E;
--notion-purple_background_co: #2B2431;
--notion-teal_background_co: #222B26;
--notion-yellow_background_co: #392E1E;
--notion-orange_background_co: #38281E;
--notion-brown_background_co: #2F2723;
--notion-gray_background_co: #191919;
}
}
OG Image#
After tinkering with the above parts, I took a day off, but today I felt the itch to look at the project files and found the ogImageGenerateURL
in the blog.config.js
file. This is also one of the features of nobelium, which comes with Open Graph, but after tinkering, I discovered some issues.
In the original file, it was filled with a domain that craigary deployed, referenced in the Container.js
file Ln39:
<meta
property="og:image"
content={`${BLOG.ogImageGenerateURL}/${encodeURIComponent(
meta.title
)}.png?theme=dark&md=1&fontSize=125px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fvercel-triangle-white.svg&images=https%3A%2F%2Fgithub.com%2FLeonWong0609%2Fnobelium%2Fraw%2Fmain%2FNobelium-Logo.svg&widths=200&widths=200&heights=undefined&heights=200`}
/>
This means that the OG image will point to a domain that includes the UrlEncoded article title and some other content. It can be inferred that it should be similar to Gravatar, which can directly provide OG images through URLs.
So I found the original repository on GitHub, which is also a Vercel project: Open Graph Image as a Service. In short, it allows you to quickly generate images with text through URLs, so you don't have to manually create each OG image. You can either access the webpage to create it or use a specific URL to return a specific image. It can be likened to the API for converting airport subscription nodes.
However, the URL provided by craigary does not have the nobelium logo and only displays text. I guessed that the specified logo URL might have expired.
Visiting the official Vercel link, it can display and customize the logo, but it does not support Chinese fonts:
So another tinkering opportunity arose!
I browsed the official repository issues and found that #108 mentioned a Korean display issue. Sure enough, it was due to missing fonts. In this thread, hoosan's reply detailed his solution for the same Japanese font issue. It seems that this can also be applied to Chinese. So I planned to deploy one myself and modify it to solve the problem.
First, we need to deploy the original vercel/og-image project ourselves. As before, fork the official repository, then follow the README instructions to modify the vercel.json
file. I directly deleted the architecture and region lines, lowered the memory a bit, and kept the rewrite unchanged. The final file looks like this:
{
"functions": {
"api/**": {
"memory": 1024
}
},
"rewrites": [
{ "source": "/(.+)", "destination": "/api" }
]
}
Now we should be able to go to the Vercel website to start deployment. The steps are the same as before, still importing from GitHub, selecting other frameworks, and then letting Vercel deploy automatically. After successful deployment, we can open the webpage to take a look and then start further modifications.
First, we need to add font files.
Google Fonts provides various Noto fonts, and those squares displayed due to missing fonts are commonly referred to as tofu. The reason this font is called Noto is to solve the tofu problem, so using this font feels quite appropriate. I noticed that other font files in the project are in woff2 format, but Google Fonts does not support downloading in woff2 format, so I did some searching and found google-webfonts-helper, which allows you to directly download woff2 format font files.
I chose to download the Noto Sans SC medium font, checked the Chinese character set and 500 weight, and scrolled to the bottom of the page to download all font files directly. After successfully downloading, I extracted the files and kept the woff2 format files for backup.
For convenience, I renamed the font file to noto-sans-sc-medium.woff2
and added it to my project's api/_fonts
folder.
Then, open the template.ts
file in the api/_lib
folder and declare a new const to read the font file we just added:
const noto = readFileSync(`${__dirname}/../_fonts/noto-sans-sc-medium.woff2`).toString('base64');
Scroll down in the same file and add the following CSS code in the appropriate position:
@font-face {
font-family: 'Noto Sans SC';
font-style:normal;
font-weight: medium;
src: url(data:font/woff2;charset=utf-8;base64,${noto}) format('woff2');
}
Next, scroll down and add the newly added font in the heading class's CSS:
After saving all these modifications, push the changes to GitHub, and Vercel should automatically start deploying again. Once the deployment is complete, open the webpage and try inputting Chinese.
Great, an automatic OG image generation site with both images and Chinese support is complete. Next, I made some small adjustments, using a dark background, adding a nobelium icon, and slightly increasing the font size, which looks something like this:
However, just operating on the front end is not our goal; we want to create a link that automatically generates the corresponding OG image based on the article title. So we click on the image on the right to copy the address, and we get a URL like this:
https://og-image-l3on.vercel.app/**Hello**%20World%20%E4%B8%AD%E6%96%87%E6%B5%8B%E8%AF%95.png?theme=dark&md=1&fontSize=125px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fvercel-triangle-white.svg&images=https%3A%2F%2Fgithub.com%2FLeonWong0609%2Fnobelium%2Fraw%2Fmain%2FNobelium-Logo.svg&widths=200&widths=200&heights=undefined&heights=200
You can see that the **Hello**%20World%20%E4%B8%AD%E6%96%87%E6%B5%8B%E8%AF%95
part should be our title after being UrlEncoded, and .png
is the selected file type, while theme=dark
, md=1
, fontSize=125px
, etc., are our other custom settings. Therefore, what we need to do now is modify the blog.config.js
and Container.js
files to use our own domain name and settings.
First, change the ogImageGenerateURL
in the blog.config.js
file to the domain of the og-image we deployed (which Vercel will automatically assign), and then go to the Container.js
file, find the <meta>
tag with the property og:image
, select the content including .png?theme=
, and replace it with the corresponding content from the URL we just obtained. The Twitter Card settings can also be modified in the same way. My settings look like this:
<meta
property="og:image"
content={`${BLOG.ogImageGenerateURL}/${encodeURIComponent(
meta.title
)}.png?theme=dark&md=1&fontSize=125px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fvercel-triangle-white.svg&images=https%3A%2F%2Fgithub.com%2FLeonWong0609%2Fnobelium%2Fraw%2Fmain%2FNobelium-Logo.svg&widths=200&widths=200&heights=undefined&heights=200`}
/>
<meta
name="twitter:image"
content={`${BLOG.ogImageGenerateURL}/${encodeURIComponent(
meta.title
)}.png?theme=dark&md=1&fontSize=125px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fvercel-triangle-white.svg&images=https%3A%2F%2Fgithub.com%2FLeonWong0609%2Fnobelium%2Fraw%2Fmain%2FNobelium-Logo.svg&widths=200&widths=200&heights=undefined&heights=200`}
/>
Here, the content is first taken from the ogImageGenerateURL
in the blog.config.js
, followed by a slash, then the UrlEncoded article title, and finally a string representing specific styles, forming a complete dynamic OG image generation URL.
After saving the modifications and pushing to GitHub, wait for Vercel to complete the deployment, then open the blog, and use the inspection tool to see the OG image link. Test it with Telegram and Twitter, and it perfectly meets expectations!
Advantages#
Opening the nobelium webpage, the first feeling is speed. Compared to the static blog previously hosted on GitHub Pages, this is much faster, with almost no loading time; content displays immediately after clicking a link.
For comparison, here is the Lighthouse score of the original Gridea static blog:
Secondly, using Notion as a CMS is also more convenient than editing Markdown files in Gridea. In the past, although I could write a blog post elsewhere and then move it to Gridea, the final publishing step still had to be done on the computer. Moreover, if the blog post had images, I had to solve the image issue, either using an image hosting service or importing each image file into Gridea. Additionally, my Gridea often failed to push successfully due to network issues, so I had to click sync in the client, exit, check whether the static page files in the output folder were updated successfully, and then manually push with Git. Compared to editing in Notion and simply changing the tags to Published to achieve publishing, it is indeed much more convenient.
However, the nobelium solution is not perfect; there are still a few aspects that make me miss the Markdown files of Gridea.
First, flexibility is somewhat limited. Markdown files can directly be compatible with HTML, so sometimes some individual elements that need customization (like badges or centered text) can be directly written in HTML. However, in nobelium, the content of the blog posts is actually the content of Notion pages, and it does not support all block types in Notion (for example, the newly launched simple tables and collapsible headings are not yet supported), and text cannot be centered, etc. For simple text and image blogs, occasionally inserting a video is completely sufficient, but if there are higher requirements for controlling the blog page, the method of using Notion in nobelium may not be as flexible as directly using Markdown.
Additionally, nobelium needs to fetch content from Notion, so it usually requires a public Notion database. If you really choose to use Notion as the content management system for the blog (using tags to distinguish published and draft content), then a public database may expose some unpublished content on the internet. Personally, I have no concerns in this regard, as I rarely write directly in Notion. Noblilium also provides a method to use NOTION_ACCESS_TOKEN
to fetch content, which allows you to access Notion content without sharing the nobelium Notion database. It looks like accessing Notion content with a token, which is valid for 180 days. For someone like me with a goldfish brain, I naturally wouldn't choose this method. According to the author's roadmap, nobelium should support fetching content from Notion's official API in the future, and recently the Notion API has officially ended its beta phase, so I look forward to updates from craigary!
Finally, my original Gridea site has been polished for a long time, and many details already meet my preferences. This could be considered a "sunk cost" of migrating to nobelium. I cannot abandon the original Wonder Space, which has been refined for nearly two years, along with all the styles I added, the Telegram instant preview template, OG attributes, and previous comments from netizens.
Domain Name#
After tinkering with the above things, two days have passed, but at that time, I had no intention of stopping. One day, after seeing the blog of Wang Linde, I noticed that his original GitHub Page had its own domain name, which reminded me that I had always wanted a domain name. That day, it was already two or three in the morning, but I still decided to start searching for information on purchasing a domain name.
Previously, I thought about buying a domain name on sites like GoDaddy, which required foreign currency payment methods, and I was discouraged when I saw that. However, after searching that day, I found that various domestic cloud services could directly purchase domain names, so I searched for l3on
on Alibaba Cloud's Wanwang. I was surprised to find that I had always thought domain names cost at least a few meals a year, but when I searched for the l3on.site
prefix, I found that this cool-looking domain name only costs 6 yuan a year... I regretted not acting sooner and placed an order for a year.
Purchasing on Wanwang only requires an Alipay account to log in, and you can also use the phone number linked to Alipay for real-name authentication. I accepted this convenience at the cost of privacy (after all, my blog content is quite benign), uploaded my identity information, and completed the real-name authentication in less than half an hour. By then, it was already past four in the morning, and the system indicated that the real-name authentication might take 24-48 hours. I thought I would sleep and tinker again in a few days.
The next day, I woke up to find an email in my inbox. At 6:50 that morning, I received the message that my real-name authentication was approved. I hope it was machine recognition; otherwise, it was a bit too early for Alibaba employees to start work.
Binding the domain name on Vercel is actually very simple. You just need to go to the project settings, fill in your domain name, and then add a CNAME record in the domain's DNS settings. Out of my tinkering nature, I also changed the domain's DNS to Cloudflare, and after adding the CNAME, the blog.l3on.site
domain took effect in less than five minutes. I remember at that time, I was staring at the screen, watching this string of characters representing my corner in the vast internet world in the browser's address bar, refreshing the page repeatedly, feeling happier little by little.
The original GitHub Page can also have a custom domain, which is as simple as Vercel. You just need to create a CNAME
file in the root directory of the repository, with the content being your domain name. I chose ws.l3on.site
, or you can directly fill in your domain name in Gridea's remote settings and push the changes to the remote. Then, add a CNAME record in Cloudflare pointing to the original github.io
domain. Now, I have two similar corners on the internet.
Summary#
So should I keep one of these two blogs and give up the other? For now, I feel I can keep both. Gridea feels more like a complete blog site with a higher degree of customization, but the corresponding management is relatively complex; nobelium is powerful and quick, allowing me to experience some relatively advanced technologies and bringing me the joy of learning new things over several nights. Therefore, I don't think it's necessary to force myself to choose one.
Because for me, blogging is neither my job nor my burden; it is more of a toy. Even a child wouldn't refuse two toys, so of course, I want both.
However, I also don't want to focus solely on these things and forget about the content. Technology has a strange allure for me, always making me want to try new things, but when I encounter difficult problems, I often become lazy about deep systematic learning. Perhaps this is why I have long been a jack of all trades.
While organizing my blog this time, I looked back at my first blog post and remembered that my original intention was to have a place to write down what I have learned and thought. The previous two casual blogs were more in line with the image of a blog I had in mind, sharing some of my most wanted words with myself and a group of invisible readers.
So a few days ago, after tinkering with the blog, I felt exhausted, and amidst the happiness was a bit of guilt. I was afraid I was losing focus, only knowing how to tinker without doing the real work. Although I felt a sense of accomplishment from doing many things, I also felt that if I didn't record anything, even the things I thought I had learned would slowly be forgotten, making all these time-consuming activities seem meaningless. Therefore, I am very grateful that I impulsively started typing today, writing over ten thousand words in one go from morning until late at night, and I was surprised by myself.
In the coming period, I may need to step back from my hobbies and shift more energy to some real-life issues, so this long article feels like a final celebration before settling down.
Finally, if you have read this far, thank you. If this article has provided you with even a little bit of information or help, it is my honor. No matter where you see this article, feel free to leave a message for communication, and you are also welcome to visit my other corner.