This is a NextJS-based application for the Hardhat documentation website. This app is utilizing SSG for creating pages on the build step. It's developed keeping the best practices in mind including accessibility, SEO and Performance optimizations, and scalability
The app mainly provides two types of pages:
- landing pages (see home page)
- documentation pages (see documentation section)
Landing pages are composed of reusable blocks and separate content files. Blocks are full-width React Components that can be stacked to compose a page. Blocks output content passed to them via props.
Documentation pages are generated from markdown files located in the src/content
folder. This folder has a nesting structure that is mapped to the page URLs on the website.
It's assumed that the app will be hosted on the Vercel platform which is highly optimized for SSG apps.
We consider two directions of the follow-up application growing:
- by creating and editing new content.
- by adding new features to the app.
The first one can be provided by working with human-friendly file formats located in the content folder (MD and YAML). Only minimal tech knowledge is needed for that. The second way requires developers' efforts.
https://hardhat-lime.vercel.app/
Website content is located in *.md
files within src/content
folder. It's written in Markdown syntax. Folders structure in content
is reflected on the website.
To tune pages, behavior and appearance also use optional *.yaml
files with additional configurations.
To preview content locally, launch the app with pnpm dev
and open http://127.0.0.1:3000 in your browser. See details in Development section.
All content is organized by hierarchy levels and the top-level entries are layouts. The layout represents a set of folders and provides navigation within them. Currently, a folder should belong to one of the layouts. In terms of UI, the layout is equal to a sidebar navigation menu with two-level items. Layouts settings can be found in the src/content/layouts.yaml
file. It contains all layouts (currently "documentation" and "tutorial"). Each layout can have the following settings:
- title (optional)
- folders - the list of folders should be included in this layout
The next level is a folder. It can contain nesting folders and *.md
files. Each *.md
file represents a single documentation page. Folders usually are represented in a sidebar as a group of items with a common title - each item in this group opens a separate file in the folder. Folders can be nested but it only affects the page's path on the website, sidebar navigation is always of two levels. To configure folders we're using _dirinfo.yaml
files which can contain the following settings:
section-title: the title of a group in the sidebar. It's optional, if skipped the folder name will be used.
section-type: this setting controls the appearance of the group in the sidebar. It can be:
- group - a regular group with a title and list of items
- single - good for groups with a single item
- hidden - the folder won't be shown in the sidebar but when you open a page from this group sidebar is present.
- plugins - the "special" group with is generated not from the
*.md
files located in the content folder, but from README.md files from plugin packages
order: an array of items in the order they should appear in the sidebar group. This is optional but if it's not specified the order will be based on file names. This array can contain two types of items:
- simple href strings (which are the same as a path to a file without a file extension. e.g.
/explanation/mining-modes
). Note it shouldn't contain the group folder name in the path. In this case, the title of the item will be generated automatically and will be the same as a page title. - objects with
href
andtitle
keys. In this case, href can be any valid relative link. The title specifies the title of that item in the sidebar. Note: this allows to specify anchor links e.g."#quick-start"
or a "index" links -/
.
All documentation content is represented by *.md
files with Markdown syntax. Besides the base Markdown syntax, we support the following features:
- MD Directives. Used to represent Admonition components (currently
Tip
andWarning
). - Code syntax highlighting
- Line highlighting in code blocks.
- Code tabs with alternative languages
Redirects allow you to redirect an incoming request path to a different destination path. Redirects settings are located in redirects.config.js
file. It exports an array of objects. Each object represents a single redirect option. We utilize NextJS Redirects API for that.
We use the plugin remark-directive
in order to provide tabs functionality.
tabsgroup
is a wrapper that wraps a group of tabs and sets all possible values of tabs.
tab
is a wrapper that wraps a codeblock (or some other element).
::::tabsgroup{options=npm,yarn}
:::tab{value=npm}
// codeblock or some other element
:::
:::tab{value=yarn}
// codeblock or some other element
:::
::::
Parameters are passed in curly braces.
options-list
- required parameter. Comma separated strings, which is provided in tabs' value
parameter.
value
- required parameter. It should be provided as an option in options-list
in tabsgroup
.
You can use space
symbol in parameters by wrapping options/value
in quotes(""
). Examples: options="npm 7+,yarn"
/ value="npm 7"
.
The front matter must be the first thing in the markdown file and must take the form of valid YAML set between triple-dashed lines. Here is an example:
---
title: Overview
description: Hardhat | Ethereum development environment for professionals
prev: false
next: false
---
You can specify title
, description
for SEO manually or disable prev
and next
links for Footer Navigation by using Front Matter.
This website is an SSG application based on Next.js. To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
First, install dependencies:
cd docs
pnpm install
Then, run the development server:
pnpm dev
Open http://127.0.0.1:3000 with your browser to see the result.
You can start editing the page by modifying src/pages/...
. The page auto-updates as you edit the file.
When developing the application you might need these main folders
- src/components - React Components for rendering pages
- src/components/landingBlocks - "Building blocks" for creating landing pages
- src/components/mdxComponents - Components used to render markdown content
- src/components/ui - common UI components
- src/hooks - common React hooks
- src/model - business logic files
- src/pages - NextJS pages.
[...docPath].tsx
means multiple pages will be generated. Page routes are based on the relative paths - src/styles - global CSS styles
- src/config.ts - keep main information about the app.
- public/ - static files
- .storybook/ - Storybook settings
- next.config.js - NextJS config
- redirects.config.js - Custom redirects
You can use Storybook to develop components in isolation. Launch it via pnpm storybook
and open http://127.0.0.1:6006/ You will see a list of components in the left-side sidebar. When you edit component's code and save it, the Storybook is auto-updating it.
Each component can be exposed with different states (stories) independently by passing props you need. You can find component stories settings in Component.stories.ts
files.
We also deploy updated storybook on each build. You can find it on https://hardhat-storybook.netlify.app/
There are two relatively independent processes in the build step:
- Generating pages themselves. We get page paths directly from the files located in the content folder. Their paths are mapped to the page routes. Layout settings don't affect to page's existence.
- Generating layouts and mapping layouts to pages. For that, we're checking which folders belong to what layout and assigning that layout to a page
Page paths are generated in the getStaticPaths
functions in files of the page
folder. The result of these functions is an array of page paths. Page pros are generated with the getStaticProps
function which is executed once per page with a page path passed as an argument and returns all required page props.
Execution of getStaticPaths
and getStaticProps
is handled by NextJS on a build step and it runs them in isolation (which means we can't share common calculated parameters within them). To optimize building time, we store an intermediate config in a temporary file on the getStaticPaths
execution and read it from getStaticProps
functions. It contains layout settings and a map of pages with specific props.
We utilize Linaria for styling components. It has the "Styled Components" syntax but generates css without runtime with works fine with SSG sites.
The documentation section is Themable. A user can switch between light, dark and high contrast themes for their convenience. There is also an "Auto" setting when theme is selected based on a user system settings.
Theming solution provides abilities to switch themes, keep the selected value in user's local storage, seamlessly keep selected page on navigating and page refreshing.
We manage themes by applying a CSS class to the HTML body. Each component has special selectors in its CSS to reflect change color depending on selected theme. To support themes, components should provide styles for all app themes (add selectors and specify colors).
Landing pages don't support themes.
Landing pages contains special "blocks" see src/components/landingBlocks. To create a new landing page start from copying /pages/index.tsx
and src/content/home.ts
. You can create another page by reodering existing blocks and passing another content to them. If necessary create new landing blocks.
We use two CI/CD providers:
- Github actions for launching code checks
- Vercel to deploy app
Each branch triggers its own process on CI/CD so you can see code check details on Github and preview the current branch on Vercel.