Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
f7c81bf
cleanup from base-stack
abrulic Jul 14, 2025
6e8dfcf
sidebar initial working version
abrulic Jul 14, 2025
58084d7
functional table of content component
abrulic Jul 19, 2025
dfbeff7
small change in the knip.json
abrulic Jul 19, 2025
8dede72
custom script to validate content folder positions inside frontmatter…
abrulic Jul 20, 2025
8d06e3e
updated readme.md file
abrulic Jul 20, 2025
d0ab191
updated readme.md file
abrulic Jul 20, 2025
03dcf30
small refactoring and added documnetation for some components
abrulic Jul 20, 2025
6d23125
footer component
abrulic Jul 20, 2025
9fb1e8c
initial theme switcher, changes on UI, additional components
abrulic Jul 22, 2025
b8d8d87
fonts, some fixes
abrulic Jul 23, 2025
4b4bf90
small change
abrulic Jul 23, 2025
67d4cb7
convention xx-file-name.mdx update - still updates needed
abrulic Jul 24, 2025
0fd0ec2
refactoring
abrulic Jul 24, 2025
3029d09
small changes and updated package.json
abrulic Jul 25, 2025
d7286cc
ts fix?
abrulic Jul 25, 2025
c9d553c
ts fix?
abrulic Jul 25, 2025
2c04dc6
ts fix?
abrulic Jul 25, 2025
e70c006
ts fix?
abrulic Jul 25, 2025
06780d2
ts fix?
abrulic Jul 25, 2025
de6cfdd
sidebar fix
abrulic Jul 27, 2025
eb3f746
added content folder and some fixes and improvements in UI
abrulic Jul 28, 2025
0e29737
removed _index route, added index.mdx file for hopemage, reorganized …
abrulic Jul 29, 2025
d6b73ed
refactoring
abrulic Aug 1, 2025
892c14c
small update in update-frontmatter logic
abrulic Aug 1, 2025
7d2842e
small refactoring
abrulic Aug 3, 2025
5eaed66
small ui improvements
abrulic Aug 3, 2025
1d3e062
small ui improvements
abrulic Aug 3, 2025
82a39a9
refactoring
abrulic Aug 4, 2025
a4e605c
refactoring
abrulic Aug 7, 2025
c960702
theme toggle fix
abrulic Aug 7, 2025
02369ea
refactoring ad vitest tests for some helper functions
abrulic Aug 8, 2025
6e2bfde
small refactoring
abrulic Aug 8, 2025
d17c3be
fix in breadcrumbs building
abrulic Aug 8, 2025
8f460c3
fix in breadcrumbs building
abrulic Aug 8, 2025
45158bb
unit tests and small improvements
abrulic Aug 10, 2025
9e5044c
comments and tests
abrulic Aug 10, 2025
05f4738
fixed so it doesnt contains v1.0.1 now
abrulic Aug 13, 2025
5045de0
fixes
abrulic Aug 13, 2025
b9f2941
small update
abrulic Aug 13, 2025
ffc1fb7
small fixes
abrulic Aug 13, 2025
22ce1df
small change
abrulic Aug 13, 2025
45bab85
small fix in sidebar
abrulic Aug 19, 2025
679a95a
refactoring
abrulic Aug 20, 2025
f3a6b68
improvements
abrulic Aug 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# Add your env variables here

# The environment of the app. This is different from NODE_ENV, and will be used for deployments.
# Default: development
#APP_ENV="staging"
GITHUB_OWNER="github-owner" # Your username or organization name (Optional. For edit/report an issue for the documentation page)
GITHUB_REPO="github-repo" # Repository name (Optional. For edit/report an issue for the documentation page)
APP_ROOT_PATH="/path/to/your/app" # Optional. Default is `process.cwd()`
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,7 @@ blob-report
*.sqlite
*.sqlite3
*.db-journal


# Content collections output files
.content-collections
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
}
},
"editor.formatOnPaste": true
}
153 changes: 85 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,89 @@
# Welcome to Forge 42 Documentation Template

This template is designed to support a flexible content structure using `.md` and `.mdx` files organized into folders. It enables deeply nested sections and subsections, making it easy to manage complex documentation with a clear and scalable hierarchy.

The project is built using the [@forge-42/base-stack](https://github.com/forge-42/base-stack) and leverages the [content-collections](https://github.com/sdorra/content-collections).


## Documentation Template Structure Overview

`app/`

This folder contains React Router v7 web application folders and files, including components and UI primitives for the documentation site’s interface, internal hooks and utilities, and the tailwind.css file for styling.


`resources/`

This folder contains all the resources used by the documentation site, such as SVG icons, fonts, and other assets.

`content/`

This folder contains sections and subsections with .mdx files that hold your documentation content. Below is the recommended structure to follow.


An example of a valid content/ folder structure for organizing your package documentation:

<p align="middle">
<img width="900px" height="500px" src="./public/base-stack.png" />
</p>

# Welcome to Forge 42 base-stack

This is a base-stack for Forge 42 projects. This stack is a starting point for all Forge 42 stacks with more
advanced features. This is an ESM Vite stack with Remix.run / React Router v7.

It includes a basic setup for a project with react-router v7 framework mode and:
- React 19 & react-compiler
- TypeScript
- TailwindCSS
- Vite
- Vitest (unit tests)
- Scripting
- Biome (linter & formatter)
- i18n support (client and server)
- Icons spritesheet generator
- lefthook hooks
- CI checks for quality control
- react-router-devtools
- Hono server
- .env var handling for server and client
- SEO robots.txt, sitemap-index and sitemap built in.

## Internationalization

This stack uses i18next for internationalization. It supports both client and server side translations.
Features included out of the box:
- Support for multiple languages
- Typesafe resources
- client side translations are fetched only when needed
- language switcher
- language detector (uses the request to detect the language, falls back to your fallback language)

## Hono server

This stack uses Hono for the server. More information about Hono can be found [here](https://honojs.dev/).
Another important thing to note is that we use a dependency called `react-router-hono-server` which is a wrapper for Hono that allows us to use Hono in our React Router application.

The server comes preconfigured with:
- i18next middleware
- caching middleware for assets
- easily extendable global application context
- .env injection into context

In order to add your own middleware, extend the context, or anything along those lines, all you have to do is edit the server
inside the `entry.server.tsx` file.

## .env handling

This stack parses your `.env` file and injects it into the server context. For the client side, in the `root.tsx` file, we use the `useLoaderData` hook to get the `clientEnv` from the server and set it as a global variable on the `window` called `env`.
If you need to access the env variables in both environments, you can create a polyEnv helper like this:
```ts
// app/utils/env.ts
// This will return the process.env on the server and window.env on the client
export const polyEnv = typeof process !== "undefined" ? process.env : window.env;
```
The server will fail at runtime if you don't set your `.env` file properly.
content/
├── _index.mdx
├── 01-changelog.mdx
├── 02-introduction.mdx
├── 03-overview.mdx
├── 04-getting-started/
│ ├── index.md
│ ├── 01-installation.mdx
│ ├── 02-quick-start.mdx
│ └── 03-project-setup.mdx
└── 05-core-features/
├── index.md
├── 01-authentication.mdx
├── 02-authorization.mdx
├── 03-data-management/
│ ├── index.md
│ ├── 01-fetching-data.mdx
│ └── 02-caching-strategies.mdx
└── 04-ui-components/
├── index.md
├── 01-buttons.mdx
└── 02-modals.mdx
```
- Top-level .mdx files (like 01-changelog.mdx) are allowed.
- Sections (like 04-getting-started, 05-core-features) are subfolders inside the content/ folder.
- Subsections (like 03-data-management, 04-ui-components) are nested folders within sections.
- Each section or subsection should have an index.md file for its sidebar title.

### Example of the valid `02-introduction.mdx` file:
```
---
title: "Introduction to Forge42 Base Stack"
summary: "Overview of the Stack"
description: "Get started with the Forge42 Base Stack — a modern web app starter template designed for speed, scalability, and developer experience."
---

## What is Forge42 Base Stack?

The Forge42 Base Stack is a full-featured web application starter template. It combines modern tools and technologies like **Remix**, **Tailwind CSS**, **TypeScript**, **Vitest**, and **React Aria Components** to help you build accessible and scalable web apps quickly.

This documentation will guide you through setting up the project, understanding its structure, and customizing it for your needs.

## Installation

To get started with the base stack, simply clone the repository and install dependencies:

```bash
npx degit forge42/base-stack my-app
cd my-app
npm install
```

### Example of the valid `04-getting-started/index.md` file:
```
---
title: Getting Started
---

```


## Getting started

Expand All @@ -71,17 +95,10 @@ pnpm install
```
3. Read through the README.md files in the project to understand our decisions.

4. Run the cleanup script:
```bash
pnpm cleanup
```

This will remove everything in the project related to the base-stack like README.md etc.
This is the first thing you should run after initializing the project.
After it is run it will remove itself from the package.json.
4. Add `content` folder

5. Start the development server:
```bash
pnpm run dev
```
6. Happy coding!
5. Happy coding!
45 changes: 45 additions & 0 deletions app/components/code-block/code-block-diff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* This module provides utilities for processing and styling lines of a text diff.
*/
const DIFF_STYLES = {
added: {
backgroundColor: "var(--color-diff-added-bg)",
borderLeft: "2px solid",
borderLeftColor: "var(--color-diff-added-border)",
indicator: "+",
},
removed: {
backgroundColor: "var(--color-diff-removed-bg)",
borderLeft: "2px solid",
borderLeftColor: "var(--color-diff-removed-border)",
indicator: "-",
},
normal: {
backgroundColor: "transparent",
borderLeft: "none",
borderLeftColor: "transparent",
indicator: "",
},
} as const

type DiffType = keyof typeof DIFF_STYLES

const DIFF_PATTERNS = {
"+ ": "added",
"- ": "removed",
} as const

type DiffPatternPrefix = keyof typeof DIFF_PATTERNS

const isDiffPatternPrefix = (prefix: string): prefix is DiffPatternPrefix => {
return prefix in DIFF_PATTERNS
}

export const getDiffType = (line: string): DiffType => {
const prefix = line.trimStart().slice(0, 2)
return isDiffPatternPrefix(prefix) ? DIFF_PATTERNS[prefix] : "normal"
}

export const cleanDiffLine = (line: string) => line.replace(/^(\s*)[+-] /, "$1")

export const getDiffStyles = (diffType: DiffType) => DIFF_STYLES[diffType]
66 changes: 66 additions & 0 deletions app/components/code-block/code-block-elements.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { ComponentPropsWithoutRef } from "react"
import { cn } from "~/utils/css"
import { createLineData } from "./code-block-parser"
import { getTokenColor, isTokenType, type tokenize } from "./code-block-syntax-highlighter"

const TokenElement = ({ token }: { token: ReturnType<typeof tokenize>[0] }) => {
const { type, value } = token
const color = isTokenType(type) ? getTokenColor(type) : ""

return <span style={{ color }}>{value}</span>
}

const DiffIndicator = ({ indicator }: { indicator: string }) => (
<span className="absolute top-0 left-0 w-4 select-none text-center font-medium text-[var(--color-diff-indicator)]">
{indicator}
</span>
)

const LineElement = ({ line }: { line: string }) => {
const { tokens, styles, isNormalDiff } = createLineData(line)

return (
<div className="relative">
<div
className="flex min-h-[1.5rem] items-center pr-4 pl-4"
style={{
backgroundColor: styles.backgroundColor,
borderLeft: styles.borderLeft,
borderLeftColor: styles.borderLeftColor,
}}
>
{!isNormalDiff && <DiffIndicator indicator={styles.indicator} />}
<span className="block">
{tokens.map((token, index) => (
<TokenElement key={`${token.value}-${index}`} token={token} />
))}
</span>
</div>
</div>
)
}

const CodeElement = ({ lines }: { lines: string[] }) => (
<code className="relative block">
{lines.map((line, index) => (
<LineElement key={`${index}-${line}`} line={line} />
))}
</code>
)

interface PreElementProps extends Omit<ComponentPropsWithoutRef<"pre">, "children"> {
lines: string[]
className?: string
}

export const PreElement = ({ lines, className = "", ...props }: PreElementProps) => (
<pre
{...props}
className={cn(
"relative overflow-x-auto rounded-lg border border-[var(--color-border)] bg-[var(--color-code-block-bg)] py-4 font-mono text-[var(--color-code-block-text)] text-xs leading-relaxed sm:text-sm md:text-base",
className
)}
>
<CodeElement lines={lines} />
</pre>
)
53 changes: 53 additions & 0 deletions app/components/code-block/code-block-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { cleanDiffLine, getDiffStyles, getDiffType } from "./code-block-diff"
import { tokenize } from "./code-block-syntax-highlighter"

interface CodeBlockChild {
props?: {
children?: string
}
}

export const extractCodeContent = (children: string | CodeBlockChild) => {
const code = typeof children === "string" ? children : (children?.props?.children ?? "")
return { code }
}

export const processLines = (content: string) => {
const lines = content.split("\n")
return filterEmptyLines(lines)
}

const filterEmptyLines = (lines: string[]) => {
return lines.filter((line, index, array) => {
const isLastLine = index === array.length - 1
const isEmpty = line.trim() === ""
return !(isEmpty && isLastLine)
})
}

export const createLineData = (line: string) => {
const diffType = getDiffType(line)
const cleanLine = cleanDiffLine(line)
const tokens = tokenize(cleanLine)
const styles = getDiffStyles(diffType)
const isNormalDiff = diffType === "normal"

return {
diffType,
cleanLine,
tokens,
styles,
isNormalDiff,
}
}

export const processCopyContent = (content: string): { code: string } => {
// removes diff markers from content
const code = content
.split("\n")
.filter((line) => !line.trimStart().startsWith("- "))
.map((line) => line.replace(/^(\s*)\+ /, "$1"))
.join("\n")

return { code }
}
Loading