Setting Up the Development Environment
In this article, we'll cover the process of preparing the development environment before diving into actual coding. This includes setting up code quality tools, styling infrastructure, and configuring multilingual support.
Tools for Code Style and Quality
To maintain consistent code style across different environments, I recommend using .editorconfig
and eslint
. While skipping this section won't prevent you from developing a blog, those who were put off by the eslint module in Nuxt.js v2 might be glad to know that it now works seamlessly without hindering development.
.editorconfig
.editorconfig
is a useful tool for standardizing IDE settings like indentation style (spaces vs. tabs), indentation size, trimming trailing whitespace, and more. Create a .editorconfig
file in the project's root directory with the following configuration:
root = true
[*]
indent_size = 2
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
ESLint
Originally designed as a linter to ensure code quality and a formatter to maintain code style, ESLint has since split its formatting functionality into a separate project called Stylistic to reduce maintenance costs. As a result, ESLint now focuses solely on linting, simplifying the integration of linters and formatters.
To streamline linting configuration, we'll use the Nuxt ESLint Module. Installing this module will automatically generate an eslint.config.js
file in flat config format, where you can define your preferred linting rules. I'll use the default settings for this guide.
nuxt module add eslint
Previously, I used Prettier as my formatter, but after reading Why I don't use Prettier, I switched to Stylistic. Thanks to the Nuxt ESLint module, you can easily enable Stylistic with minimal configuration.
export default defineNuxtConfig({
modules: [
'@nuxt/eslint',
],
devtools: { enabled: true },
compatibilityDate: '2024-11-01',
eslint: {
config: {
stylistic: true,
},
},
})
Setting Up the Styling Infrastructure
Styling infrastructure refers to the foundational setup for efficiently managing CSS and building a consistent design system. In this project, we’ll establish a styling infrastructure by setting up Tailwind CSS, configuring theme environments like dark mode, and defining global CSS rules to be applied across the project.
Tailwind CSS
Working with CSS directly can be complex and tedious, and using CSS preprocessors like SCSS or LESS often involves spending a lot of time on naming conventions or dealing with style conflicts due to nested selectors. To address these issues, I prefer the atomic CSS approach, which eliminates these concerns. Among the available tools, I have chosen Tailwind CSS, a well-established and reliable solution. Instead of setting up and configuring it manually, I will install the Nuxt Tailwind CSS Module.
nuxt module add tailwindcss
After installing the module, you can configure Tailwind CSS directly in nuxt.config.ts
without generating a separate configuration file. However, since this project will require extensive Tailwind CSS configurations, I’ll create a dedicated configuration file in TypeScript format.
bunx tailwindcss init --ts
Since the primary colors in a project often represent its identity, they are unlikely to change frequently. However, during the development phase, they may be updated more often. To handle this, I’ll define a primary
color property, making it easy to update the primary color whenever needed—especially useful for client requests in outsourced projects.
import type { Config } from 'tailwindcss'
import colors from 'tailwindcss/colors'
export default {
content: [],
theme: {
extend: {
colors: {
primary: colors.sky,
},
},
},
plugins: [],
} satisfies Config
Tailwind-Merge
While Tailwind CSS is convenient, it can be challenging to dynamically adjust styles based on different states. The tailwind-merge library addresses this by resolving Tailwind CSS class conflicts and managing priorities.
bun i tailwind-merge
Color Themes
Switching between light and dark themes can be easily implemented using the Nuxt Color Mode Module.
nuxt module add color-mode
For now, we’ll focus on setting up the environment, leaving the actual implementation for the next article.
Global CSS
Next, let’s write the global CSS rules that will be applied throughout the project. Since this project doesn’t require tasks complex enough for SCSS or LESS, we’ll use PostCSS.
body {
@apply antialiased text-slate-600 dark:text-slate-300 bg-white dark:bg-slate-900;
}
.dark-mode {
color-scheme: dark
}
::-moz-selection {
@apply bg-primary-500/40
}
::selection {
@apply bg-primary-500/40
}
- Lines 1–3: Apply anti-aliasing to fonts, and set the default font and background colors for light and dark themes.
- Lines 5–7: Adjust the color scheme for dark mode, including the scrollbar color to match the dark theme.
- Lines 9–15: Define styles for text highlighted by the user.
Finally, register the CSS file in Nuxt's nuxt.config.ts
to apply it globally.
export default defineNuxtConfig({
modules: [
'@nuxt/eslint',
'@nuxtjs/i18n',
'@nuxtjs/tailwindcss',
'@nuxtjs/color-mode',
],
devtools: { enabled: true },
css: ['assets/css/main.pcss'],
compatibilityDate: '2024-11-01',
eslint: {
config: {
stylistic: true,
},
},
})
Icons
In limited spaces such as headers or footers, using icons can make it easier to understand the purpose of a button while maintaining design consistency. To achieve this, we'll install the Nuxt Icon module to conveniently integrate a variety of icons into our project.
nuxt module add icon
Since I frequently use 20x20 icons, I will set the default size to 20.
export default defineNuxtConfig({
modules: [
'@nuxt/eslint',
'@nuxtjs/i18n',
'@nuxtjs/tailwindcss',
'@nuxtjs/color-mode',
'@nuxt/icon',
],
devtools: { enabled: true },
css: ['assets/css/main.pcss'],
compatibilityDate: '2024-11-01',
eslint: {
config: {
stylistic: true,
},
},
icon: {
size: '20',
},
})
You can install your preferred icon package. Personally, I find the Phosphor icon set to be the most versatile, so I will use it for this project.
bun i -D @iconify-json/ph
Setting Up a Multilingual Environment
To provide blog content in multiple languages, tasks such as routing configuration, message file management, and SEO settings are necessary. In this section, we’ll explore how to easily set up a multilingual environment using the i18n module, configure automatic redirects based on the visitor’s browser language, and more.
i18n Module
The Nuxt i18n Module allows you to effortlessly set up a multilingual environment. It can also detect the user’s browser language and automatically redirect them to their preferred language.
nuxt module add i18n
After installing the module, configure it in nuxt.config.ts
as shown below:
export default defineNuxtConfig({
modules: [
'@nuxt/eslint',
'@nuxtjs/i18n',
'@nuxtjs/tailwindcss',
'@nuxtjs/color-mode',
],
devtools: { enabled: true },
css: ['assets/css/main.pcss'],
compatibilityDate: '2024-11-01',
eslint: {
config: {
stylistic: true,
},
},
i18n: {
baseUrl: 'YOUR SITE',
locales: [
{ code: 'ko', name: 'Korean', file: 'ko.yaml', language: 'ko-KR' },
{ code: 'en', name: 'English', file: 'en.yaml', language: 'en-US' },
],
strategy: 'prefix_except_default',
defaultLocale: 'en',
detectBrowserLanguage: {
alwaysRedirect: true,
fallbackLocale: 'en',
},
vueI18n: './i18n/i18n.config.ts',
},
})
Key Settings
- baseUrl: Specifies the base URL of the site, which is essential for SEO optimization.
- locales: Defines the languages offered by the site. The
language
property should follow standard formats (ko-KR
,en-US
). - defaultLocale: Specifies the default language to use when accessing the site in an unsupported language.
- strategy: Defines the URL generation strategy based on locales. With the
prefix_except_default
strategy, URLs for non-default languages include a locale prefix, while the default language does not.- Default language (English): https://besfir-blog.pages.dev/
- Korean: https://besfir-blog.pages.dev/ko/
- detectBrowserLanguage: Detects the browser's language and redirects the user to the appropriate language page. It works out of the box at the top-level pages.
- vueI18n: Specifies the configuration file path for Vue i18n. Since Nuxt i18n internally uses Vue i18n, all its options are available.
For more details, see the i18n module options documentation.
Date Localization
In the development field, the freshness of information is critical, so it’s essential to display publication or update dates. Vue i18n provides the $d
function and the datetimeFormats
option to format dates based on locales. To apply these options globally, use the defineI18nConfig
composable to define the configuration passed to Vue i18n.
export default defineI18nConfig(() => ({
datetimeFormats: {
ko: {
short: {
year: 'numeric', month: 'short', day: 'numeric',
},
long: {
year: 'numeric',
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric',
hour12: true,
},
},
en: {
short: {
year: 'numeric', month: 'short', day: 'numeric',
},
long: {
year: 'numeric',
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric',
},
},
},
}))
Creating Locale Files
To avoid errors, make sure the locale files specified in the i18n
> locales
property of nuxt.config.ts
configuration exist. Below are examples with dummy data:
hello: 안녕하세요.
Basic SEO Setup
Providing multilingual content requires SEO optimization tailored to each language. The i18n module offers a SEO guide for adding relevant settings. Instead of using a layout component, I applied the settings directly in app.vue
.
<script setup lang="ts">
const head = useLocaleHead()
</script>
<template>
<Html
:lang="head.htmlAttrs?.lang"
:dir="head.htmlAttrs?.dir"
>
<Head>
<template
v-for="link in head.link"
:key="link.id"
>
<Link
:id="link.id"
:rel="link.rel"
:href="link.href"
:hreflang="link.hreflang"
/>
</template>
<template
v-for="meta in head.meta"
:key="meta.id"
>
<Meta
:id="meta.id"
:property="meta.property"
:content="meta.content"
/>
</template>
</Head>
<Body>
<nuxt-page />
</Body>
</Html>
</template>
The code above is a basic setup for global settings, and we'll be setting up SEO for each page in its own page component.