Writing Guide
Guidelines and recommendations for authoring blog posts on the Svelte Vietnam Blog
Table of contents
Technical Design Overview
The Svelte Vietnam Blog is a static site: its data is part of the source code, and blog post contents are written in Markdown and Svelte syntax. The process of writing and publishing a blog post shares much similarities with a typical daily software development workflow and does not depend on any third party database or fancy editor (WYSIWYG, Markdown editor, rich text, etc.). In a way, the Svelte Vietnam Blog uses Git as its CMS.
This design requires writers to have some basic experience with web development, but it also helps reduce significantly the infrastructure cost and human resources needed to maintain the blog.
For non-technical writers
If you are looking for ways to contribute to the Svelte Vietnam Blog but are not familiar with the
technical aspects listed here, please feel free to reach out to administrators via the official Svelte Vietnam Discord server, or send your blog post
in any traditional text format (txt
, pdf
, docx
, etc.) to blog@sveltevietnam.dev.
Directory Structure of a Blog Post
Each blog post is a collection of files located under a sites/sveltevietnam.dev/src/data/blog/posts/:id
directory:
sites/sveltevietnam.dev/src/data/blog/posts/:id
│
├── metadata.ts <-- main data definition
│
├── content/
┆ │
┆ ├── en.md.svelte <-- content in English
┆ └── vi.md.svelte <-- content in Vietnamese
│
├── images/
┆ │
┆ └── thumbnail.jpg <-- thumbnail and social sharing image
┆ └── ... <-- any other images used in the post
┆
├── ... <-- other data used in the post
Overview of the writing process
Step 1: Install Necessary Software
- node.js:
- The required node.js version for the project is defined in package.json under the "engines" key.
- It is recommended you use volta to manage
node.js
versioning. If installed, volta will automatically set the appropriate version for the project based on the definition inpackage.json
.
- lefthook: a git hook manager that automatically runs code convention & sanity checks before committing code.
- pnpm: a package manager that serves as an alternative to
npm
.
You of course also need git and a code editor of your choice. Setting up code editor is unfortunately out of the scope of this document, but there should be plenty resources on the web for that.
Step 2: Set up Local Development Environment
Clone the source code:
git clone https://github.com/sveltevietnam/sveltevietnam.dev.git
git clone git@github.com:sveltevietnam/sveltevietnam.dev.git
gh repo clone sveltevietnam/sveltevietnam.dev
Run the following setup command at the root directory of the project:
pnpm boot
Start the development server at the
sites/sveltevietnam.dev
directory:cd sites/sveltevietnam.dev pnpm dev
If everything is set up correctly, you can now access a local copy of the Svelte Vietnam Blog at http://localhost:5005/en/blog.
Step 3: Initialize Post Data
In your terminal, get to the sites/sveltevietnm.dev
directory. If this is your first time
contributing content for sveltevietnam.dev, initialize your author data with the following command:
pnpm create:person
Next, initialize blog post data with the following command:
pnpm create:post
Step 4: Complete your Post Content
If you are using VSCode, open the project at the root directory instead of sites/sveltevietnam.dev
, so that the editor settings under .vscode
can be read.
Following step 3, you will need to finish you writing at two newly created files:
.../content/en.md.svelte
: your writing in English,.../content/vi.md.svelte
: your writing in Vietnamese.
You may refer to "Post Content Syntax: Markdown + Svelte" for examples and guidelines on special syntax and features available in post content, as well as "Updating Post Metadata" on how to edit post metadata.
Step 5: Create a Pull Request
Once you are satisfied with your writing, you will need to perform typical source control operations
with git
and Github, including committing your changes onto a new branch and creating a pull request targeting the default branch (the branch displayed when you visit the sveltevietnam/sveltevietnam.dev repository).
You may also use Github CLI to simplify certain workflows. For example, after installation and login (gh auth
),
you can create a pull request quite conveniently with the following command:
gh pr create
Refer to DEVELOPMENT.md - Commit Message Guidelines for guidelines on writing commit messages. Workflow with git
and Github are not particularly difficult, but require several steps and can be achieved in
different ways, so we will not go into details here. If you need assistance or have questions,
please contact our administrators via the Discord - Discussion - Blog channel.
Step 6: Review and Publish
After your pull request is sent, administrators will review your writing and involve any necessary discussion / further enquiry right in the pull request thread on Github.
Once review is completed, administrators will merge your pull request, shortly after which your post will go live on the Svelte Vietnam Blog 🎉.
Post Content Syntax: Markdown + Svelte
Post content is parsed by a custom Svelte preprocessor. Feel free to check out the source code of this preprocessor to learn more about its inner workings. In short:
- most of the content is written in Github Flavored Markdown syntax,
- you can also use Svelte syntax, such as the
script
tag,enhanced:img
, or other imported Svelte components, - syntax highlighting for code blocks is handled by Shiki.
Several special syntax have been developed to make writing blog posts more convenient. These special syntax are introduced in the next sections.
Code Block
For technical blog posts, sharing code snippets is expected. To display a code block, you can use the following syntax:
```ts title="basic.ts"
console.log('Hello, world!');
```
The above content will be displayed as:
console.log('Hello, world!');
Indicating Code Changes (diff)
To indicate code changes, you can wrap the code block with comments using the :::diff +
or :::diff -
directive. For example, the following content...
```svelte title="diff.svelte"
<script>
// :::diff -
import ComponentOld from './old.svelte';
// :::
// :::diff +
import ComponentNew from './new.svelte';
// :::
</script>
<!-- :::diff - -->
<ComponentOld>...</ComponentOld>
<!-- ::: -->
<!-- :::diff + -->
<ComponentNew>...</ComponentNew>
<!-- ::: -->
```
...will be displayed as:
<script>
import ComponentOld from './old.svelte';
import ComponentNew from './new.svelte';
</script>
<ComponentOld>...</ComponentOld>
<ComponentNew>...</ComponentNew>
Highlighting and Focusing Code
To set emphasis on one or more lines in the code block, you can wrap the code block with comments using the :::highlight
or :::focus
directive. For example, the following content...
```css title="highlight-focus.css"
.example {
/* :::highlight error */
@extend mx-auto; /* not supported */
/* ::: */
/* :::highlight warning */
@apply mx-auto; /* not recommend */
/* ::: */
/* :::highlight */
margin: 0 auto; /* good */
/* ::: */
/* :::highlight success */
/* better */
margin-left: auto;
margin-right: auto;
/* ::: */
/* :::focus */
margin-inline: auto; /* best */
/* ::: */
}
```
...will be displayed as:
.example {
@extend mx-auto; /* not supported */
@apply mx-auto; /* not recommend */
margin: 0 auto; /* good */
/* better */
margin-left: auto;
margin-right: auto;
margin-inline: auto; /* best */
}
Grouping Multiple Code Blocks
To display multiple code blocks that share the same context, you can use the enhanced-code-block
element with the group
attribute. For example, the following content...
<enhanced-code-block group display="tabs">
```bash title="npm"
npm install --save-dev @sveltevietnam/markdown
```
```bash title="yarn"
yarn add -D @sveltevietnam/markdown
```
```bash title="pnpm"
pnpm add -D @sveltevietnam/markdown
```
</enhanced-code-block>
...will be displayed as a tab group:
npm install --save-dev @sveltevietnam/markdown
yarn add -D @sveltevietnam/markdown
pnpm add -D @sveltevietnam/markdown
Additionally, to group code blocks as a collection of files, you can set display="files"
. For
example, the following content...
<enhanced-code-block group display="files">
```html title="example.html"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example</title>
<link rel="stylesheet" href="./example.css">
<script module src="./example.js"></script>
</head>
```
```css title="example.css"
.example {
color: red;
}
```
```js title="example.js"
console.log('Hello, world!');
```
</enhanced-code-block>
...will be displayed as a file group:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example</title>
<link rel="stylesheet" href="./example.css">
<script module src="./example.js"></script>
</head>
.example {
color: red;
}
console.log('Hello, world!');
Note: The empty lines surrounding the enhanced-code-block
opening and closing tags and
its content are necessary.
Images
The the time of this writing, the project does not support Markdown syntax for images in blog posts. Instead, please use HTML tags as shown in the following example...
<script>
import illustration1 from '../images/blog.png?format=avif';
</script>
<figure>
<img src={illustration1} class="mx-auto max-w-full" width="573" height="376" alt="illustration: a handwriting in a rectangle box that reads 'Blog'" />
<figcaption>Illustration 1: Example for displaying an image in post content</figcaption>
</figure>
...which will be displayed as:

Recommendations:
- images should be placed in the
.../images
directory, similar tothumbnail.jpg
, as shown in the "Directory Structure of a Blog Post" section, - use the import query
format=avif
to optimize for image size. For more information, refer to imagetools - directives, - set appropriate
width
,height
, andalt
attributes for the image.
Callout
Callout enhances a text content with some contextual meaning and helps draw user's attention. The following example shows one possible variant of callout:
To display the above callout, you can use the following syntax:
<div class="c-callout c-callout--success c-callout--icon-trophy">
Content to be highlighted...
</div>
Use the playground below to find the right classes for your desired callout.
=> Class names to apply: c-callout
c-callout--info
.
This is a sample “callout” that enhances a text block with some contextual meaning and helps draw user's attention. See more examples and how to use it via "Svelte Vietnam Blog - Writing Guidelines".
Data Definition & Edit
Following "Overview of the writing process", post and author data can be
initialized with the help of the pnpm create:post
and pnpm create:person
commands. However, you
may review and edit this data at any time.
Updating Post Metadata
As mentioned in "Directory Structure of a Blog Post",
data definition for a blog post is located at sites/sveltevietnam.dev/src/data/blog/posts/:id/metadata.ts
.
For example, the metadata for the post "Come for Svelte, Stay for the
Web" is defined as follows:
import { defineBlogPostMetadata } from '..';
export default defineBlogPostMetadata((lang) => ({
publishedAt: new Date('2024-04-20'),
authors: ['vnphanquang'],
categories: ['svelte-and-kit', 'ecosystem'],
...(
{
en: {
slug: '20240420-come-for-svelte-stay-for-the-web',
title: 'Come for Svelte, Stay for the Web',
description:
'Svelte is exceptionally good at staying out of your way, allowing you to focus on building a better web. No one cares about frameworks anyway!',
keywords: 'web, ecosystem, runtime, compile-time, action',
readMinutes: 8,
numWords: 1590,
translation: 'manual',
},
vi: {
slug: '20240420-den-voi-web-thong-qua-svelte',
title: 'Đến với web thông qua Svelte',
description:
'API của Svelte được thiết kế để thân thiện với các công nghệ và kiến thức nền tảng, giúp bạn dễ dàng tập trung vào việc xây dựng ứng dụng web, thay vì quan tâm đến framework này và nọ.',
keywords: 'web, hệ sinh thái, runtime, compile-time, action',
readMinutes: 8,
numWords: 1990,
translation: 'original',
},
} as const
)[lang],
}));
Once you have completed the writing process (step 1 -> 4), make sure you revisit the corresponding .../metadata.ts
and update the necessary fields, such as readMinutes
and numWords
.
Post Thumbnail
To set a custom thumbnail for a blog post, simply add the image as thumbnail.jpg
to the .../images
directory, as listed in "Directory Structure of a Blog
Post". Some guidelines and recommendations are as follows:
- the image should be in JPEG format and named
thumbnail.jpg
, - it should have a minimum width of 2240px,
- it should have an aspect ratio of 16:9,
- it should be minimalistic, and without too much text.
Updating Author Data
Author data is defined at sites/sveltevietnam.dev/src/data/people/:id/index.ts
. For example, data
for "Quang Phan" is defined as follows:
import { definePerson } from '..';
export default definePerson((lang) => ({
links: {
website: 'https://vnphanquang.com',
bluesky: 'https://bsky.app/profile/vnphanquang.com',
github: 'https://github.com/vnphanquang',
},
...{
en: {
name: 'Quang Phan',
description: 'Developer, administrator',
},
vi: {
name: 'Phan Quang',
description: 'Lập trình viên, quản trị viên',
},
}[lang],
}));