initial commit after project creation

This commit is contained in:
Gerhard Scheikl
2026-04-01 09:38:50 +02:00
commit b02af637d4
292 changed files with 61408 additions and 0 deletions

17
.env.example Normal file
View File

@@ -0,0 +1,17 @@
# These are retrieved from your project at app.tina.io
NEXT_PUBLIC_TINA_CLIENT_ID=***
TINA_TOKEN=***
# This is set by default CI with Netlify/Vercel/Github, but can be overriden
NEXT_PUBLIC_TINA_BRANCH=***
# This is being used for the sitemap
NEXT_PUBLIC_SITE_URL=***
# This displays the Theme selection's dropdown
NEXT_PUBLIC_ENABLE_THEME_SELECTION=***
NEXT_PUBLIC_GTM_ID=***
# Optional: Add these to specify GitHub repository details for fetching metadata like last edited date and author.
# Create one at https://github.com/settings/tokens with 'public_repo' scope
GITHUB_TOKEN=***
GITHUB_REPO=***
GITHUB_OWNER=***

2
.github/.env vendored Normal file
View File

@@ -0,0 +1,2 @@
NEXT_PUBLIC_BASE_PATH='/tinadocs'
NEXT_PUBLIC_SITE_URL='https://tina.io'

1
.github/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
!.env

39
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- 'Type: Dependencies'
commit-message:
prefix: "⬆️ Actions"
rebase-strategy: auto
# Maintain dependencies for npm
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
labels:
- 'Type: Dependencies'
commit-message:
prefix: "⬆️ NPM"
rebase-strategy: auto
allow:
- dependency-name: "tinacms"
- dependency-name: "@tinacms/*"
ignore:
- dependency-name: "yup"
groups:
tinacms:
patterns:
- "@tinacms/*"
- "tinacms"

127
.github/workflows/build-and-deploy.yml vendored Normal file
View File

@@ -0,0 +1,127 @@
# Sample workflow for building and deploying a Next.js site to GitHub Pages
#
# To get started with Next.js see: https://nextjs.org/docs/getting-started
#
name: Deploy Next.js site to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: [main]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect package manager
id: detect-package-manager
run: |
if [ -f "${{ github.workspace }}/yarn.lock" ]; then
echo "✅ Detected yarn"
echo "manager=yarn" >> $GITHUB_OUTPUT
echo "command=install" >> $GITHUB_OUTPUT
echo "runner=yarn" >> $GITHUB_OUTPUT
exit 0
elif [ -f "${{ github.workspace }}/pnpm-lock.yaml" ]; then
echo "✅ Detected pnpm"
echo "manager=pnpm" >> $GITHUB_OUTPUT
echo "command=install" >> $GITHUB_OUTPUT
echo "runner=pnpm" >> $GITHUB_OUTPUT
exit 0
elif [ -f "${{ github.workspace }}/package.json" ]; then
echo "✅ Detected npm"
echo "manager=npm" >> $GITHUB_OUTPUT
echo "command=ci" >> $GITHUB_OUTPUT
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
exit 0
else
echo "❌ Unable to determine package manager"
exit 1
fi
- name: Setup pnpm
if: steps.detect-package-manager.outputs.manager == 'pnpm'
uses: pnpm/action-setup@v4
with:
version: 9.15.2
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: ${{ steps.detect-package-manager.outputs.manager }}
- name: "Setup Pages #1"
run: |
touch public/.nojekyll
- name: "Setup Pages #2"
uses: actions/configure-pages@v5
with:
# Automatically inject basePath in your Next.js configuration file and disable
# server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
#
# You may remove this line if you want to manage the configuration yourself.
static_site_generator: next
- name: Restore cache
uses: actions/cache@v4
with:
path: |
.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock') }}-
- name: Install dependencies
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
- name: Build with Next.js & TinaCMS
run: ${{ steps.detect-package-manager.outputs.runner }} export
env:
NEXT_PUBLIC_TINA_CLIENT_ID: ${{ secrets.NEXT_PUBLIC_TINA_CLIENT_ID }}
NEXT_PUBLIC_TINA_BRANCH: ${{ github.ref_name }}
TINA_TOKEN: ${{ secrets.TINA_TOKEN }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./.next
search-tests:
needs: build
uses: ./.github/workflows/search-test.yml
with:
base_url: https://tina-docs-red.vercel.app/
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

29
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Lint and Check
on:
pull_request:
branches: [main]
jobs:
lint-and-check:
name: Run Biome Lint and Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
- name: Enable Corepack
run: corepack enable
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Biome Lint & Formatter
run: pnpm lint src/ tina/

76
.github/workflows/pr-open.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Build Pull request
on:
pull_request:
types: [opened, synchronize, reopened]
env:
NEXT_PUBLIC_TINA_CLIENT_ID: ${{ secrets.NEXT_PUBLIC_TINA_CLIENT_ID }}
TINA_TOKEN: ${{ secrets.TINA_TOKEN }}
NEXT_PUBLIC_TINA_BRANCH: ${{ github.head_ref }}
jobs:
build:
runs-on: ubuntu-latest
name: Build sample
outputs:
NEXT_PUBLIC_BASE_PATH: ${{ steps.setup-base-path.outputs.NEXT_PUBLIC_BASE_PATH }}
steps:
- uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- uses: pnpm/action-setup@v4
name: Install pnpm
id: pnpm-install
with:
version: 9.15.2
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Load .env file
uses: xom9ikk/dotenv@v2
with:
path: ./.github
- name: Setup Base Path
id: setup-base-path
run: |
echo "NEXT_PUBLIC_BASE_PATH=${{ env.NEXT_PUBLIC_BASE_PATH }}" >> $GITHUB_OUTPUT
- name: Build
run: pnpm build
slugify-branch:
runs-on: ubuntu-latest
outputs:
slug: ${{ steps.slug.outputs.slug }}
steps:
- name: Slugify branch name
id: slug
run: |
SLUG=$(echo "${{ github.head_ref }}" | tr '/' '-' | tr -d '.' | tr '[:upper:]' '[:lower:]')
echo "slug=$SLUG" >> $GITHUB_OUTPUT
search-tests:
needs: [build, slugify-branch]
uses: ./.github/workflows/search-test.yml
with:
base_url: https://tina-docs-git-${{ needs.slugify-branch.outputs.slug }}-tinacms.vercel.app
NEXT_PUBLIC_BASE_PATH: ${{ needs.build.outputs.NEXT_PUBLIC_BASE_PATH }}

89
.github/workflows/search-test.yml vendored Normal file
View File

@@ -0,0 +1,89 @@
name: Search Functionality Tests
on:
workflow_dispatch:
inputs:
base_url:
description: 'Base URL to test (required)'
required: true
type: string
workflow_call:
inputs:
base_url:
description: 'Base URL to test (required)'
required: true
type: string
NEXT_PUBLIC_BASE_PATH:
description: 'Base path (required)'
required: true
type: string
jobs:
test-search:
name: Test Search Functionality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.2
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run search tests
run: pnpm test
env:
BASE_URL: ${{ inputs.base_url }}
NEXT_PUBLIC_BASE_PATH: ${{ inputs.NEXT_PUBLIC_BASE_PATH || '' }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: |
test-results/
playwright-report/
retention-days: 7
report-results:
name: Report Test Results
runs-on: ubuntu-latest
needs: [test-search]
if: always()
steps:
- name: Download test results
uses: actions/download-artifact@v4
with:
path: test-results/
- name: Create test summary
run: |
echo "## Search Functionality Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f "test-results/test-results/test-results/results.json" ]; then
echo "### Search Tests ✅" >> $GITHUB_STEP_SUMMARY
echo "Search tests completed successfully." >> $GITHUB_STEP_SUMMARY
else
echo "### Search Tests ❌" >> $GITHUB_STEP_SUMMARY
echo "Search tests failed or were not run." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "Check the artifacts for detailed test reports." >> $GITHUB_STEP_SUMMARY

48
.gitignore vendored Normal file
View File

@@ -0,0 +1,48 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
/public/pagefind/
# misc
.DS_Store
*.pem
robots.txt
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel
.yarn
# exports
/public/exports
#sitemaps
/public/sitemap.xml
/public/sitemap-0.xml
/playwright-report
/test-results

30
.nftignore Normal file
View File

@@ -0,0 +1,30 @@
# Next.js serverless function ignore file
# Exclude webpack cache and build artifacts
.next/cache/**
.next/static/**
.next/server/chunks/**
# Exclude git files
.git/**
.github/**
# Exclude public assets (except those needed by API)
public/img/**
public/admin/**
# Exclude large dependencies not needed in serverless functions
node_modules/@swc/core-linux-x64-gnu/**
node_modules/@swc/core-linux-x64-musl/**
node_modules/@esbuild/**
node_modules/webpack/**
node_modules/terser/**
node_modules/monaco-editor/**
# Exclude development files
*.map
*.development.js
# Exclude documentation and readme files
*.md
README*
CHANGELOG*

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
lts/*

8
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"recommendations": [
"bradlc.vscode-tailwindcss",
"biomejs.biome",
"graphql.vscode-graphql",
"biomejs.biome-vscode"
]
}

23
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,23 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "always"
},
"editor.defaultFormatter": "biomejs.biome",
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
"node_modules": true
},
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true,
"**/.turbo/**": true,
"**/.next/**": true
}
}

126
AGENTS.md Normal file
View File

@@ -0,0 +1,126 @@
# AGENTS.md
This file provides guidance to AI coding agents working in this repository.
## About TinaDocs
TinaDocs is a **public starter/template** — anyone can fork it and deploy their own documentation site. Keep this in mind when making changes:
- Never hardcode deployment-specific values (URLs, org names, branding). Use environment variables or TinaCMS settings instead.
- New features that not every user will want must be toggleable via TinaCMS settings or environment variables — don't assume all consumers want the same feature set.
- Avoid dependencies on specific hosting providers. Support multiple deployment modes (Vercel, GitHub Pages, static export).
## Architecture
TinaDocs is a documentation platform built on **Next.js 15 (App Router)** with **TinaCMS** for git-based content management, **Pagefind** for static search, and **Tailwind CSS** with a 6-theme system.
**Content flow:** MDX files in `content/docs/` → TinaCMS schema (`tina/collections/`) → auto-generated GraphQL client (`tina/__generated__/`) → Next.js pages. Never edit files in `tina/__generated__/`.
### Key directories
- `src/app/` — Next.js App Router pages and API routes
- `src/components/tina-markdown/` — Markdown rendering: `standard-elements/` (headings, code blocks, tables) and `embedded-elements/` (API refs, callouts, recipes)
- `tina/collections/` — TinaCMS collection schemas (docs, API schemas, navigation, settings)
- `tina/templates/markdown-embeds/` — Embeddable content templates (accordion, callout, code-tabs, card-grid, etc.)
- `tina/customFields/` — Custom CMS field components (theme selector, file upload, Monaco editor)
- `src/styles/global.css` — Theme definitions via CSS custom properties (6 themes, light/dark)
## Commands
```bash
pnpm install # Install deps (pnpm 9.15.2 required)
pnpm dev # Dev server with Turbopack (localhost:3000, CMS at /admin)
pnpm build # Production build (TinaCMS + Next.js + Pagefind + sitemap)
pnpm lint # Biome linter check
pnpm lint:fix # Auto-fix lint issues
pnpm test # Playwright E2E tests (Chromium)
pnpm test:ui # Playwright interactive UI
pnpm build-local-pagefind # Rebuild search index locally
npx playwright test tests/e2e/some-test.spec.ts # Run a single test
```
## Coding Standards
- Use `@/` path aliases for imports: `@/components`, `@/utils`, `@/app`, `@/tina`, `@/services`, `@/hooks`, `@/styles`, `@/content`, `@/lib`, `@/types`, `@/config`
- Use Biome for formatting: 2-space indent, double quotes, semicolons, trailing commas (ES5)
- No `console.log` — use `noConsole: error`
- No `.forEach()` — use `for...of` or `.map()`
- Self-close empty JSX elements
## Key Patterns
### Fetching TinaCMS data in pages
```typescript
import { fetchTinaData } from "@/services/tina/fetch-tina-data";
import client from "@/tina/__generated__/client";
async function getData(slug: string) {
return await fetchTinaData(client.queries.docs, slug);
}
export default async function DocsPage({
params,
}: {
params: Promise<{ slug: string[] }>;
}) {
const { slug } = await params;
const data = await getData(slug.join("/"));
return (
<TinaClient
Component={Document}
props={{ query: data.query, variables: data.variables, data: data.data }}
/>
);
}
```
### Adding a new embeddable template
1. **Define template** in `tina/templates/markdown-embeds/my-embed.template.tsx`:
```typescript
export const MyEmbedTemplate = {
name: "myEmbed",
label: "My Embed",
fields: [
{ type: "string", name: "title", label: "Title" },
{ type: "rich-text", name: "body", label: "Body" },
],
};
```
2. **Create component** in `src/components/tina-markdown/embedded-elements/my-embed.tsx`:
```typescript
import { tinaField } from "tinacms/dist/react";
import { TinaMarkdown, type TinaMarkdownContent } from "tinacms/dist/rich-text";
import { MarkdownComponentMapping } from "../markdown-component-mapping";
export default function MyEmbed(props: { title: string; body?: TinaMarkdownContent }) {
return (
<div data-tina-field={tinaField(props, "title")}>
<h3>{props.title}</h3>
<TinaMarkdown content={props.body as TinaMarkdownContent} components={MarkdownComponentMapping} />
</div>
);
}
```
3. **Register template** in `tina/collections/docs.tsx` — add to the `templates` array in the `body` rich-text field
4. **Map component** in `src/components/tina-markdown/markdown-component-mapping.tsx`:
```typescript
myEmbed: (props) => <MyEmbed {...props} />,
```
### TinaCMS component conventions
- Use `tinaField(props, "fieldName")` on `data-tina-field` attributes for visual editing
- Render nested rich-text with `<TinaMarkdown content={...} components={MarkdownComponentMapping} />`
- Define variant/config mappings as `const` objects with `as const`
## Environment
Copy `.env.example`. Required: `NEXT_PUBLIC_TINA_CLIENT_ID`, `TINA_TOKEN`, `NEXT_PUBLIC_TINA_BRANCH` (from app.tina.io). Optional: `NEXT_PUBLIC_SITE_URL`, `NEXT_PUBLIC_GTM_ID`, `NEXT_PUBLIC_BASE_PATH`.

3
CLAUDE.md Normal file
View File

@@ -0,0 +1,3 @@
# CLAUDE.md
Get instructions from AGENTS.md

201
LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

446
README.md Normal file
View File

@@ -0,0 +1,446 @@
# 🚀 TinaDocs - Your Complete Documentation Solution
> **Modern documentation made simple and powerful**
TinaDocs is a complete documentation solution built with [Tina CMS](https://tina.io/) that combines developer-friendly tools with content creator accessibility.
![TinaDocs Starter](./public/docs-starter.png)
**Figure: Landing Page of TinaDocs**
## ✨ Why Choose TinaDocs
### **Rich Feature Set**
- **🔍 Fast search** - Powered by Pagefind for instant content discovery
- **📊 API documentation** - Generate beautiful API docs from your OpenAPI specs
- **📑 Multi-tab interfaces** - Organize complex information with tabbed layouts
- **🎨 Custom theming** - Make your docs match your brand
- **✏️ Visual editing** - Content creators can edit directly through TinaCMS
- **📱 Responsive design** - Works great on all devices
- **⚡ Performance optimized** - Built with Next.js for fast load times
### **What Makes It Special**
- **Modern stack** - Clean, maintainable codebase
- **Developer-friendly** - Easy to customize and extend
- **Content creator-friendly** - Non-technical team members can contribute
- **SEO optimized** - Built-in best practices for search visibility
---
## 🚀 Quick Start with npx
The fastest way to get started with TinaDocs is using npx:
```bash
npx create-tina-app@latest my-docs --template tina-docs
```
This command will:
- Download and set up TinaDocs in a new directory called `my-docs`
- Prompt you to select your preferred theme during setup
- Configure the basic project structure
- Install all necessary dependencies
### **Available Themes**
When using npx, you can choose from these beautiful themes:
- **Default** - Clean black and white design
- **Tina** - TinaCMS-inspired theme with orange accents
- **Blossom** - Elegant pink/rose color scheme
- **Lake** - Professional blue color palette
- **Pine** - Natural green tones
- **Indigo** - Modern purple/indigo design
---
## 📖 How to Use TinaDocs
There are two ways you can use TinaDocs:
**For developers** as a launching point to develops a highly custom docs solution. TinaCMS is based on markdown. Use this code as a basis to [implement custom components](https://tina.io/docs/reference/types/rendering-markdown#linking-to-react-components) to be used in MDX to fit your use case. Follow the Getting Started guide below.
**Quickest experience** use as is and deploy in minutes via TinaCloud for a docs setup that you still hold all the keys and data for, and get to using right away.
> 💡 TinaCMS integrates tighly with GitHub, and has a powerful [editorial workflow](https://tina.io/docs/tina-cloud/editorial-workflow) based on GitHub's branch protection features.
## 🛠️ Getting Started
### **Step 1: Install Dependencies**
> 💡 We recommend `pnpm` for faster installs. [Learn why pnpm is great](https://www.ssw.com.au/rules/best-package-manager-for-node/) for Node.js projects.
```bash
pnpm install
```
### **Step 2: Start Development Server**
```bash
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) to see your docs in action.
---
## 🌐 Production Setup
### **Step 3: Set Up GitHub and TinaCloud**
1. **Add your docs to GitHub**: Push your local repository to GitHub if you haven't already
2. **Create a TinaCloud account**: Sign up at [app.tina.io](https://app.tina.io)
3. **Link your repository**: Connect your GitHub repository to TinaCloud through the dashboard
Note: if you dont see your repo in the list, click the button “Configure your TinaCloud permissions on GitHub” at the bottom of the page
4. **Sync Media**: In your TinaCloud project, click on Media and then "Sync Media". This will import your media files (like images) from your GitHub repository into Tinas Media Manager so theyre available in the visual editor.
### **Step 4: Configure Environment**
1. Rename `.env.example` to `.env`
2. Add your Tina credentials:
```env
NEXT_PUBLIC_TINA_CLIENT_ID=<get this from app.tina.io>
TINA_TOKEN=<get this from app.tina.io>
NEXT_PUBLIC_TINA_BRANCH=<your content branch>
NEXT_PUBLIC_ENABLE_THEME_SELECTION=<display theme selector>
```
If you want to test `pnpm build` locally, you need to add:
```env
NEXT_PUBLIC_SITE_URL=http://localhost:3000
```
**Theme Preview Mode:**
To enable a theme selector in the UI for previewing all themes, add:
```env
NEXT_PUBLIC_ENABLE_THEME_SELECTION=true
```
> 💡 **Note:** The theme selector allows you to preview different themes in real-time, but these changes are temporary and won't persist when you open a new browser window/tab. To make a theme permanent, update the `Selected Them` field in your Settings through TinaCMS.
### **Step 5 (Optional): Enable GitHub Metadata**
To display commit history and last updated information on your documentation pages:
1. Create a GitHub personal access token:
- Go to [GitHub Settings > Developer settings > Personal access tokens > Tokens (classic)](https://github.com/settings/tokens)
- Click "Generate new token (classic)"
- Set the scope to `public_repo`
- Choose an expiration setting:
- Select "No expiration" for a permanent token, OR
- Choose an expiration period and set up a process to regenerate the token before it expires
- Generate the token and add it to your `.env` file
2. Add the following to your `.env` file & deployment settings:
```env
GITHUB_TOKEN=<your GitHub personal access token>
GITHUB_OWNER=<your GitHub username or organization>
GITHUB_REPO=<your repository name>
```
> 💡 **Note:** The `GITHUB_TOKEN` field is optional. However, if you choose to provide it, you must also provide `GITHUB_OWNER` and `GITHUB_REPO`. If you're deploying to Vercel, the `GITHUB_OWNER` and `GITHUB_REPO` fields will be automatically populated from Vercel's environment variables (`VERCEL_GIT_REPO_OWNER` and `VERCEL_GIT_REPO_SLUG`), so you only need to provide the `GITHUB_TOKEN`.
### **Step 6: Build for Production**
```bash
pnpm build
```
---
## 🚀 Deployment
### **Step 7: Deploy to Vercel**
TinaDocs works great with Vercel. Check out our [deployment guide](https://tina.io/docs/tina-cloud/deployment-options/vercel) for detailed instructions.
---
## 🔍 Search Setup
TinaDocs includes fast search powered by [Pagefind](https://pagefind.app/), which indexes your content during the build process.
### **How to Build the Search Index**
To generate or update the search index, run:
```bash
pnpm build-local-pagefind
```
This command:
- Builds the project
- Generates the search index
- Saves the index files to `public/pagefind/`
The search index is automatically updated every time you run the build.
---
### **Custom Theming**
Want to create your own custom theme? TinaDocs includes a comprehensive theming system that allows you to create custom color schemes. See the [Custom Theming section](#-custom-theming) below for detailed instructions on how to create and implement your own themes.
---
## 🎨 Custom Theming
TinaDocs includes a comprehensive theming system that allows you to create custom color schemes. The theme system is built on CSS custom properties (variables) and supports both light and dark modes.
### **How Themes Work**
Themes in TinaDocs are implemented using CSS custom properties defined in `src/styles/global.css`. Each theme has:
- **Brand colors** (primary, secondary, tertiary) with hover states and gradients
- **Neutral colors** for backgrounds, text, and borders
- **Glass effects** for modern UI elements
- **Dark mode variants** for each theme
### **Theme Structure**
Each theme follows this pattern in `global.css`:
```css
/* LIGHT MODE */
.theme-your-theme {
--brand-primary: #your-primary-color;
--brand-primary-hover: #your-primary-hover;
--brand-primary-light: #your-primary-light;
--brand-primary-text: #your-primary-text;
--brand-primary-contrast: #your-primary-contrast;
--brand-primary-gradient-start: #your-gradient-start;
--brand-primary-gradient-end: #your-gradient-end;
/* ... more brand colors ... */
--neutral-background: #your-background;
--neutral-surface: #your-surface;
--neutral-text: #your-text;
--neutral-border: #your-border;
--background-color: #your-page-background;
}
/* DARK MODE */
.theme-your-theme.dark {
/* Dark mode variants of all the above colors */
}
```
### **Creating a Custom Theme**
To create a new custom theme, follow these steps:
#### **1. Add CSS Variables to `src/styles/global.css`**
Add your theme's CSS variables after the existing themes:
```css
/* YOUR CUSTOM THEME */
.theme-custom {
--brand-primary: #your-primary-color;
--brand-primary-hover: #your-primary-hover;
--brand-primary-light: #your-primary-light;
--brand-primary-text: #your-primary-text;
--brand-primary-contrast: #your-primary-contrast;
--brand-primary-gradient-start: #your-gradient-start;
--brand-primary-gradient-end: #your-gradient-end;
--brand-secondary: #your-secondary-color;
--brand-secondary-hover: #your-secondary-hover;
--brand-secondary-light: #your-secondary-light;
--brand-secondary-text: #your-secondary-text;
--brand-secondary-contrast: #your-secondary-contrast;
--brand-secondary-gradient-start: #your-secondary-gradient-start;
--brand-secondary-gradient-end: #your-secondary-gradient-end;
--brand-tertiary: #your-tertiary-color;
--brand-tertiary-hover: #your-tertiary-hover;
--brand-tertiary-light: #your-tertiary-light;
--brand-tertiary-text: #your-tertiary-text;
--brand-tertiary-contrast: #your-tertiary-contrast;
--brand-tertiary-gradient-start: #your-tertiary-gradient-start;
--brand-tertiary-gradient-end: #your-tertiary-gradient-end;
--glass-gradient-start: rgba(255, 255, 255, 0.1);
--glass-gradient-end: rgba(255, 255, 255, 0.4);
--neutral-background: #your-background;
--neutral-background-secondary: #your-secondary-background;
--neutral-surface: #your-surface;
--neutral-text: #your-text;
--neutral-text-secondary: #your-secondary-text;
--neutral-border: #your-border;
--neutral-border-subtle: #your-subtle-border;
--background-color: #your-page-background;
}
.theme-custom.dark {
/* Dark mode variants - invert or adjust colors for dark mode */
--brand-primary: #your-dark-primary;
--brand-primary-hover: #your-dark-primary-hover;
/* ... continue with all other variables ... */
--neutral-background: #your-dark-background;
--neutral-surface: #your-dark-surface;
--neutral-text: #your-dark-text;
/* ... etc ... */
}
```
#### **2. Add Theme to TinaCMS Selector**
Update `tina/customFields/theme-selector.tsx` to include your new theme:
```typescript
const themes = [
// ... existing themes ...
{
value: "custom",
label: "Custom",
description: "Your custom theme description",
colors: {
primary: "#your-primary-color",
secondary: "#your-secondary-color",
accent: "#your-accent-color",
},
},
];
```
#### **3. Add Theme to UI Selector**
Update `src/components/ui/theme-selector.tsx` to include your theme in the themes array:
```typescript
const themes = ["default", "tina", "blossom", "lake", "pine", "indigo", "custom"];
```
#### **4. Apply Your Theme**
1. **Temporary preview**: Use the theme selector in the UI (if enabled)
2. **Permanent change**: Update the "Selected Theme" field in TinaCMS Settings
### **Color Guidelines**
When creating custom themes, consider these guidelines:
- **Primary colors**: Use for main actions, links, and important UI elements
- **Secondary colors**: Use for supporting elements and secondary actions
- **Tertiary colors**: Use for accents and highlights
- **Neutral colors**: Ensure good contrast ratios for accessibility
- **Dark mode**: Test your theme in both light and dark modes
### **Theme Testing**
To test your custom theme:
1. Enable theme preview: `NEXT_PUBLIC_ENABLE_THEME_SELECTION=true`
2. Use the theme selector in the bottom-right corner
3. Test in both light and dark modes
4. Verify accessibility with sufficient contrast ratios
### **Theme Examples**
The existing themes demonstrate different approaches:
- **Default**: Minimalist monochrome design
- **Tina**: Brand-focused with orange and blue
- **Blossom**: Warm, elegant pink tones
- **Lake**: Professional blue palette
- **Pine**: Natural, organic green theme
- **Indigo**: Modern purple/violet design
Each theme includes comprehensive color variations for different UI states and accessibility considerations.
---
## 🛠️ Utility Scripts
TinaDocs includes helpful utility scripts to manage your documentation project:
### **Documentation Reset**
Completely reset your documentation structure to start fresh:
```bash
pnpm run cleanup
```
**What it does:**
- ✅ Removes all directories within `content/docs/` (preserves only `index.mdx`)
- ✅ Removes all API schema files in `content/apiSchema/`
- ✅ Removes docs-assets and landing-assets image directories
- ✅ Completely removes the API tab from navigation
- ✅ Clears Next.js cache (`.next` folder) to prevent stale page references
- ✅ Provides a clean documentation slate for new content
- ✅ Validates project structure before running
-**Requires interactive confirmation** - asks for explicit "yes" to proceed
> **🚨 CRITICAL WARNING:**
>
> **This command PERMANENTLY DELETES all documentation content.**
>
> - ❌ **If you've already made changes**, running cleanup will DELETE your work
> - ✅ **Run cleanup FIRST** if you want a clean slate, then make your changes
> - ✅ **Commit your changes to git** before running cleanup if you want to preserve them
>
> **This action cannot be undone unless you have committed your changes to version control.**
> **⚠️ Important:** After running cleanup, you must restart your development server with `pnpm dev` to ensure Next.js rebuilds the site without cached references to deleted pages.
For detailed information about available scripts, see [`scripts/README.md`](./scripts/README.md).
---
## 📚 Learn More
- [Tina Documentation](https://tina.io/docs) - Explore Tina's full capabilities
- [Getting Started Guide](https://tina.io/docs/setup-overview/) - Quick setup walkthrough
- [GitHub Repository](https://github.com/tinacms/tinacms) - Contribute or report issues
---
### API Documentation
TinaDocs provides comprehensive API documentation capabilities powered by OpenAPI specifications. Generate beautiful, interactive API documentation directly from your OpenAPI/Swagger JSON files.
#### **Supported Features**
**🔄 Dynamic Content Management**
- **Upload OpenAPI specs** - Import your API specifications through TinaCMS
- **Auto-generated navigation** - Create menu structures based on API tags and endpoints
- **Bulk page generation** - Generate entire documentation sections from your spec
- **Real-time updates** - Update documentation when your API spec changes
#### **What's Not Supported**
Currently, TinaDocs does not support:
- **Header configuration APIs** - Custom header management endpoints
- **WebSocket APIs** - Real-time communication endpoints
- **GraphQL schemas** - GraphQL introspection schemas (use REST API docs instead)
#### **Getting Started with API Docs**
1. **Upload your OpenAPI spec** through the API Schema collection in TinaCMS
2. **Create API reference pages** using the API Reference component
3. **Generate navigation structure** with the "Group of API References" template
4. **Customize the display** by selecting specific endpoints and tags
For detailed instructions, see the [API Documentation Guide](content/docs/api-documentation/overview.mdx) and [OpenAPI Spec Documentation](content/docs/tinadocs-features/openapi-spec-docs.mdx).
---
**Ready to improve your documentation?** Give TinaDocs a try!

76
biome.json Normal file
View File

@@ -0,0 +1,76 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"files": {
"maxSize": 10485760,
"ignore": [
".next/**",
"node_modules/**",
"dist/**",
"public/**",
"content/**",
"coverage/**",
"tina/__generated__/**",
"tina/tina-lock.json",
"src/styles/**",
"src/utils/tina/compare-markdown.ts"
]
},
"formatter": {
"enabled": true,
"useEditorconfig": false,
"formatWithErrors": true,
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 80,
"attributePosition": "auto",
"bracketSpacing": true
},
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noConsole": "error",
"noExplicitAny": "off",
"noArrayIndexKey": "off"
},
"style": {
"useSelfClosingElements": "error",
"useTemplate": "error",
"noParameterAssign": "off"
},
"a11y": {
"useSemanticElements": "error",
"useButtonType": "error",
"noLabelWithoutControl": "off",
"useKeyWithClickEvents": "off",
"useIframeTitle": "off",
"noSvgWithoutTitle": "off"
},
"correctness": {
"noChildrenProp": "off",
"useExhaustiveDependencies": "error"
},
"complexity": {
"noForEach": "error",
"useOptionalChain": "error",
"noBannedTypes": "off"
}
}
},
"javascript": {
"formatter": {
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "es5",
"semicolons": "always",
"arrowParentheses": "always",
"bracketSameLine": false,
"quoteStyle": "double",
"attributePosition": "auto",
"bracketSpacing": true
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
---
title: Overview
last_edited: '2025-07-15T07:33:40.077Z'
---
TinaDocs supports documenting your API directly from an OpenAPI .json specification (OAS). This allows you to generate structured, editable documentation blocks that reflect real API endpoints—without manually copying and pasting details.
This tab has been used to provide some miscellaneous example documentation, based on an OAS.

View File

@@ -0,0 +1,23 @@
---
description: Multiple status values can be provided with comma separated strings
seo:
title: Finds Pets by Status
description: Multiple status values can be provided with comma separated strings
title: Finds Pets by Status
last_edited: '2025-07-15T07:40:42.267Z'
auto_generated: true
---
Multiple status values can be provided with comma separated strings
## Endpoint Details
**Method:** `GET`
**Path:** `/pet/findByStatus`
## API Reference
<apiReference schemaFile="Swagger-Petstore.json|GET:/pet/findByStatus" />

View File

@@ -0,0 +1,27 @@
---
description: >-
Multiple tags can be provided with comma separated strings. Use tag1, tag2,
tag3 for testing.
seo:
title: Finds Pets by Tags
description: >-
Multiple tags can be provided with comma separated strings. Use tag1, tag2,
tag3 for testing.
title: Finds Pets by Tags
last_edited: '2025-07-15T07:40:41.760Z'
auto_generated: true
---
Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
## Endpoint Details
**Method:** `GET`
**Path:** `/pet/findByTags`
## API Reference
<apiReference schemaFile="Swagger-Petstore.json|GET:/pet/findByTags" />

View File

@@ -0,0 +1,21 @@
---
description: 'API endpoint for POST /pet/{petId}/uploadImage'
seo:
title: Uploads an Image
description: 'API endpoint for POST /pet/{petId}/uploadImage'
title: Uploads an Image
last_edited: '2025-07-15T07:40:41.566Z'
auto_generated: true
---
## Endpoint Details
**Method:** `POST`
**Path:** `/pet/{petId}/uploadImage`
## API Reference
<apiReference schemaFile="Swagger-Petstore.json|POST:/pet/{petId}/uploadImage" />

View File

@@ -0,0 +1,21 @@
---
description: 'API endpoint for POST /pet/{petId}'
seo:
title: Updates a Pet in the Store with Form Data
description: 'API endpoint for POST /pet/{petId}'
title: Updates a Pet in the Store with Form Data
last_edited: '2025-07-15T07:40:41.851Z'
auto_generated: true
---
## Endpoint Details
**Method:** `POST`
**Path:** `/pet/{petId}`
## API Reference
<apiReference schemaFile="Swagger-Petstore.json|POST:/pet/{petId}" />

View File

@@ -0,0 +1,21 @@
---
description: API endpoint for POST /pet
seo:
title: Add a New Pet to the Store
description: API endpoint for POST /pet
title: Add a New Pet to the Store
last_edited: '2025-07-15T07:40:42.182Z'
auto_generated: true
---
## Endpoint Details
**Method:** `POST`
**Path:** `/pet`
## API Reference
<apiReference schemaFile="Swagger-Petstore.json|POST:/pet" />

View File

@@ -0,0 +1,21 @@
---
description: API endpoint for PUT /pet
seo:
title: Update an Existing Pet
description: API endpoint for PUT /pet
title: Update an Existing Pet
last_edited: '2025-07-15T07:40:41.710Z'
auto_generated: true
---
## Endpoint Details
**Method:** `PUT`
**Path:** `/pet`
## API Reference
<apiReference schemaFile="Swagger-Petstore.json|PUT:/pet" />

View File

@@ -0,0 +1,20 @@
---
seo:
title: Logs Out Current Logged in User Session
description: API endpoint for GET /user/logout
title: Logs Out Current Logged in User Session
last_edited: '2025-07-15T07:40:41.523Z'
auto_generated: true
---
## Endpoint Details
**Method:** `GET`
**Path:** `/user/logout`
## API Reference
<apiReference schemaFile="Swagger-Petstore.json|GET:/user/logout" />

View File

@@ -0,0 +1,22 @@
---
seo:
title: Create User
description: This can only be done by the logged in user.
title: Create User
last_edited: '2025-07-15T07:40:41.468Z'
auto_generated: true
---
This can only be done by the logged in user.
## Endpoint Details
**Method:** `POST`
**Path:** `/user`
## API Reference
<apiReference schemaFile="Swagger-Petstore.json|POST:/user" />

View File

@@ -0,0 +1,128 @@
---
seo:
title: Q1 Campaign Launch - Marketing Team
description: Internal guide for launching our new product features in Q1
title: Internal Document Example
last_edited: '2025-07-15T07:37:45.447Z'
---
<Callout
body={<>
🤖 This example is AI generated, any references to real places or people are incidental.
</>}
variant="warning"
/>
**Marketing Team Internal Documentation**
This guide outlines the complete campaign strategy, timeline, and deliverables for our Q1 feature launch.
<Callout
body={<>
**Campaign Deadline**: All assets must be finalized by **March 15th** for the March 20th launch.
</>}
variant="warning"
/>
## Campaign Overview
**Product**: Enhanced Analytics Dashboard\
**Target Audience**: Enterprise customers, data analysts, product managers\
**Launch Date**: March 20, 2025\
**Campaign Duration**: 8 weeks (Pre-launch + 6 weeks active)
## Key Messaging & Positioning
### Primary Message
"Transform raw data into actionable insights with our most powerful analytics dashboard yet."
### Supporting Points
* 3x faster query performance
* Real-time collaboration features
* Advanced visualization tools
* Enterprise-grade security
<Callout
body={<>
All messaging must align with our established brand voice: **Professional, innovative, and customer-focused**. Reference the brand guidelines doc for tone specifics.
</>}
variant="info"
/>
## Campaign Timeline & Deliverables
| Week | Phase | Key Deliverables | Owner | Status |
| -------- | ------------- | ----------------------------------------- | ------------ | -------------- |
| Week 1-2 | Pre-Launch | Blog posts, case studies, email sequences | Content Team | ✅ Complete |
| Week 3 | Soft Launch | Partner announcements, beta user outreach | Partnerships | 🟡 In Progress |
| Week 4 | Launch Week | Press release, social campaign, webinar | PR & Social | ⏳ Pending |
| Week 5-6 | Amplification | Customer stories, retargeting ads | Growth Team | ⏳ Pending |
| Week 7-8 | Optimization | A/B testing, performance analysis | Analytics | ⏳ Pending |
## Content Calendar
### Blog Content
* **March 5**: "The Future of Data Analytics" (Thought Leadership)
* **March 12**: "Customer Success Story: TechCorp's 200% ROI" (Case Study)
* **March 20**: "Introducing Enhanced Analytics Dashboard" (Product Launch)
* **March 27**: "5 Ways to Maximize Your Analytics ROI" (How-To Guide)
### Social Media
* **LinkedIn**: B2B focused, 3 posts/week
* **Twitter**: Industry news & quick tips, daily posts
* **YouTube**: Product demo video, launch day
<Callout
body={<>
Remember to use campaign hashtag **#DataDrivenDecisions** consistently across all channels. Pre-schedule posts using Hootsuite.
</>}
variant="success"
/>
## Budget Allocation
| Channel | Budget | Expected ROI | Notes |
| ----------------- | ----------- | ------------ | --------------------------- |
| Paid Social | $15,000 | 4:1 | Focus on LinkedIn & Twitter |
| Google Ads | $25,000 | 3.5:1 | Target high-intent keywords |
| Content Marketing | $8,000 | 6:1 | Video production & design |
| Events/Webinars | $12,000 | 5:1 | Lead generation focus |
| **Total** | **$60,000** | **4.2:1** | Q1 campaign budget |
## Success Metrics
**Primary KPIs**:
* 500 new trial sign-ups (target)
* 150 demo requests
* $200K pipeline generated
* 25% increase in organic traffic
**Secondary Metrics**:
* Email open rates >22%
* Social engagement +40%
* Webinar attendance >300
* Press coverage in 5+ publications
<Callout
body={<>
Track all metrics in our **Campaign Dashboard**. Weekly check-ins every Tuesday at 10 AM to review performance and adjust tactics.
</>}
variant="idea"
/>
## Emergency Contacts & Escalation
**Campaign Manager**: Sarah Kim (@sarah-kim, ext. 2845)\
**Creative Director**: Mike Chen (@mike-creative, ext. 2901)\
**PR Lead**: Jessica Torres (@jess-pr, ext. 2756)\
**Analytics**: David Park (@david-analytics, ext. 2612)
***
*Document Owner: Marketing Team | Next Review: March 1, 2025*

View File

@@ -0,0 +1,211 @@
---
seo:
title: TinaDocs Type Definition
title: Library (API) Example
last_edited: '2025-07-15T07:43:31.405Z'
tocIsHidden: false
---
> This example content is a random page taken from the [PlateJS documentation](https://platejs.org/docs/api/slate/location-ref).
Location references are objects that keep specific locations (paths, points, or ranges) in a document synced over time as new operations are applied to the editor.
You can access their `current` property at any time for the up-to-date location value. You can access their `current` property at any time for the up-to-date location value.
## Types
***
#### `PathRef`
Path reference objects keep a specific path in a document synced over time. Created using `editor.api.pathRef`.
<typeDefinition
property={[
{
name: "current",
description: "The current path value, updated as operations are applied.\n",
type: "Path | null",
required: true,
experimental: false,
typeUrl: ""
},
{
name: "affinity",
description:
"The direction to prefer when transforming the path:\n\n* 'forward': Prefer the position after inserted content\n* 'backward': Prefer the position before inserted content\n* null: No preference\n",
type: "'forward' | 'backward' | null",
required: false,
experimental: false
},
{
name: "unref( )",
description:
"Call this when you no longer need to sync this path. Returns the final path value.\n",
type: "( ) => Path | null"
}
]}
/>
#### `PointRef`
Point reference objects keep a specific point in a document synced over time. Created using `editor.api.pointRef`.
<typeDefinition
property={[
{
name: "current",
description:
"The current point value, updated as operations are applied.\n",
type: "Point | null",
required: true,
experimental: false,
typeUrl: ""
},
{
name: "affinity",
description:
"The direction to prefer when transforming the point:\n\n* 'forward': Prefer the position after inserted content\n* 'backward': Prefer the position before inserted content\n* null: No preference\n",
type: "'forward' | 'backward' | null",
required: false,
experimental: false
},
{
name: "unref( )",
description:
"Call this when you no longer need to sync this point. Returns the final point value.\n",
type: "( ) => Point | null"
}
]}
/>
#### `RangeRef`
Range reference objects keep a specific range in a document synced over time. Created using `editor.api.rangeRef`.
<typeDefinition
property={[
{
name: "current",
description:
"The current range value, updated as operations are applied.\n",
type: "TRange | null",
required: true,
experimental: false,
typeUrl: ""
},
{
name: "affinity",
description:
"The direction to prefer when transforming the range:\n\n* 'forward': Both points prefer after inserted content\n* 'backward': Both points prefer before inserted content\n* 'inward': Range tends to stay same size when content is inserted at edges\n* 'outward': Range tends to grow when content is inserted at edges\n* null: No preference\n",
type: "'forward' | 'backward' | 'inward' | 'outward' | null",
required: false,
experimental: false
},
{
name: "unref( )",
description:
"Call this when you no longer need to sync this range. Returns the final range value.\n",
type: "( ) => TRange | null"
}
]}
/>
Example usage of a RangeRef:
```typescript
const selectionRef = editor.api.rangeRef(editor.selection, {
affinity: 'inward',
})
// Operations that might change the selection
Transforms.unwrapNodes(editor)
// Restore the selection using the ref
Transforms.select(editor, selectionRef.unref())
```
## `PathRefApi`
***
#### `transform`
Transform a path reference by an operation.
<typeDefinition
property={[
{
name: "ref",
description: "The path reference to transform.\n",
type: "PathRef",
required: true,
experimental: false,
typeUrl: ""
},
{
name: "op",
description:
"The operation to apply. The editor calls this automatically as needed.\n",
type: "Operation",
required: true,
experimental: false
}
]}
/>
## `PointRefApi`
***
#### `transform`
Transform a point reference by an operation.
<typeDefinition
property={[
{
name: "ref",
description: "The point reference to transform.\n",
type: "PointRef",
required: true,
experimental: false,
typeUrl: ""
},
{
name: "op",
description:
"The operation to apply. The editor calls this automatically as needed.\n",
type: "Operation",
required: true,
experimental: false
}
]}
/>
## `RangeRefApi`
***
#### `transform`
Transform a range reference by an operation.
<typeDefinition
property={[
{
name: "ref",
description: "The range reference to transform.\n",
type: "RangeRef",
required: true,
experimental: false,
typeUrl: ""
},
{
name: "op",
description:
"The operation to apply. The editor calls this automatically as needed.\n",
type: "Operation",
required: true,
experimental: false
}
]}
/>

View File

@@ -0,0 +1,6 @@
---
title: Pet Store All Routes
last_edited: '2025-07-15T07:34:30.319Z'
---
<apiReference schemaFile="Swagger-Petstore.json" />

View File

@@ -0,0 +1,118 @@
---
title: Deploying Your Docs
last_edited: '2025-10-27T04:14:52.046Z'
auto_generated: false
---
Once you've set up your TinaDocs site locally and configured TinaCloud, the next step is deploying it to a hosting provider. This guide will walk you through the process, with a focus on deploying to Vercel.
### Prerequisites
Before deploying, ensure you have:
1. A TinaCloud project set up (see [Setting Up TinaCloud](/docs/tinacloud/overview))
2. Your repository fully configured with TinaCMS
3. Environment variables for `NEXT_PUBLIC_TINA_CLIENT_ID` and `TINA_TOKEN`
### Setting Up Environment Variables
For your deployment to work correctly, you must set the following environment variables on your hosting provider:
```javascript
NEXT_PUBLIC_TINA_CLIENT_ID=<your-client-id>
TINA_TOKEN=<your-read-only-token>
```
You can find these values in your TinaCloud dashboard:
* Client ID: In the "Overview" tab of your project
* Token: In the "Tokens" tab of your project
### Configuring Your Build Command
To ensure the TinaCMS admin interface is built alongside your site, your build command should run `tinacms build` before your site's build command:
```bash
tinacms build && next build
```
This creates the admin interface at `/admin/index.html` in your final build.
### Deploying to Vercel (Recommended)
Vercel is the recommended hosting provider for TinaDocs sites due to its seamless integration with Next.js.
#### Step 1: Connect Your Repository
1. Log in to [Vercel](https://vercel.com)
2. Click "Add New Project"
3. Connect your GitHub repository
4. Select the repository containing your TinaDocs site
#### Step 2: Configure Build Settings
In the configuration screen:
1. Leave the framework preset as "Next.js"
2. Modify the build command if needed:
* If your package.json has a build script like `"build": "tinacms build && next build"`, you can leave the default setting
* Otherwise, set the build command to: `tinacms build && next build`
![Vercel Build Settings](/img/vercel-build-settings.png)
#### Step 3: Set Environment Variables
Add your TinaCMS environment variables:
1. Expand the "Environment Variables" section
2. Add the following variables:
* `NEXT_PUBLIC_TINA_CLIENT_ID` = Your TinaCloud Client ID
* `TINA_TOKEN` = Your TinaCloud Read-Only Token
3. Add any other environment variables your project requires
#### Step 4: Deploy
Click "Deploy" and Vercel will build and deploy your TinaDocs site.
#### Step 5: Configure Branch Settings (Optional)
For production branch settings:
1. Go to your project settings in Vercel
2. Navigate to "Git" section
3. Configure your production branch (typically "main")
### Other Deployment Options
#### Netlify
1. Connect your GitHub repository to Netlify
2. Set the build command to: `tinacms build && next build`
3. Set the publish directory to: `out` (for static export) or `.next` (for server rendering)
4. Add the TinaCMS environment variables in the Netlify dashboard
#### GitHub Pages
1. Create a GitHub workflow file (`.github/workflows/deploy.yml`)
2. Configure the workflow to set environment variables
3. Use the `tinacms build && next build` command
4. Set up GitHub Pages to deploy from your build output
### Testing Your Deployment
After deploying, verify that:
1. Your site is accessible at your domain
2. The TinaCMS admin interface works at `/admin/index.html`
3. You can log in and make edits that are saved to your repository
### Troubleshooting
If you encounter issues:
* **Admin page not found**: Ensure your build command includes `tinacms build`
* **Authentication failures**: Check that your environment variables are correctly set
* **Content not updating**: Verify your TinaCloud project has the correct site URL configured
* **Branch issues**: Ensure your branch configuration in `tina/config.js` is correct
For more details, visit the [official TinaCMS deployment documentation](/docs/tinacloud/deployment-options/vercel).

View File

@@ -0,0 +1,80 @@
---
title: Configuring TinaCloud
last_edited: '2025-08-07T23:37:23.124Z'
auto_generated: false
---
<Callout
variant="idea"
body={<>
Rather watch a video? Check out our video below.
</>}
/>
<youtube embedSrc="https://www.youtube.com/embed/sTVd8CYtXrA?si=XjFqX35NwcNgVhP3" caption="" />
TinaCloud connects your website to GitHub via an indexed datalayer, allowing editors to make changes directly through your site's interface.
You can also set this up yourself with custom database, git and auth logic.
**If using GitHub, we recommend going with TinaCloud**. To read more about TinaCloud, see the [TinaCloud documentation](https://tina.io/docs/tina-cloud/introduction).
## Create Your TinaCloud account
1. Go to [https://app.tina.io/signin](https://app.tina.io/signin).
2. Click "Authenticate with GitHub".
3. Sign in with your GitHub account.
4. Authorise Tina.
You should now be redirected to the TinaCloud dashboard.
## Creating a Project in TinaCloud
1. **Authorize GitHub**: When setting up a project, you'll need to authenticate with GitHub. A popup window will ask for permission to give Tina.io access to your GitHub repositories.
2. **Choose Your Repository**: After GitHub authorization, select the repository that contains your site's content. If you don't see your repository in the list, you may need to reconfigure your Tina.io permissions in GitHub.
3. **Configure the Project**: Set up the following properties:
* **Project Name**: This appears when users log in to the project
* **Site URL(s)**: Enter both your local URL (e.g., `http://localhost:3000`) and production URL (e.g., `https://yourdomain.com`)
* **Glob Patterns**: If you're using dynamic preview deployments, you can enter patterns like `https://<PROJECT-NAME>-*-<ACCOUNT-OWNER>.vercel.app`
4. **Sync Media**: Click on the 'Media' tab and then 'Sync Media'. This will import your media files from your GitHub repository into Tina's Media Manager so they're available in the visual editor.
## Connecting Your Project to the Editor
Once your project is created, you'll need to connect it to your site:
1. **Run the Backend Init Command**:
```bash
npx @tinacms/cli init backend
```
This command will:
* Prompt for your Client ID (found in the "Overview" tab of your project)
* Prompt for a Read Only Token (found in the "Tokens" tab)
* Populate your `.env` file with the necessary environment variables
1. **Update Your Configuration**: Ensure your `tina/config.js` file correctly references the environment variables:
```javascript
export default defineConfig({
token: process.env.TINA_TOKEN,
clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID,
branch: process.env.NEXT_PUBLIC_TINA_BRANCH || '',
// other config options...
})
```
> **Important**: These environment variables must also be set in your deployment workflow. See our [deployment guide](/docs/set-up/deploying-your-docs) for more information.
By following these steps, your TinaCloud project will be connected to your repository, enabling content editing directly through your site's interface.
## Using the Editor with TinaCloud
Edits made via the TinaCloud (as opposed to the local) editor are automatically pushed to your Git repository.
Access the local editors by running `pnpm run dev` in your terminal and going to `http://localhost:3000/admin/index.html`.
Access the TinaCloud editor by going to `https://<DOMAIN>/admin/index.html` on your deployed site.
You can also access the TinaCloud editor by running `pnpm run build`, then `pnpm run start`, and then going to `http://localhost:3000/admin/index.html`.

View File

@@ -0,0 +1,54 @@
---
title: Custom Datalayer
last_edited: '2025-07-15T06:59:09.720Z'
---
Setting up your own datalayer gives you complete control over how TinaCMS interacts with your content. This guide provides an overview of the key components and concepts.
## Core Components
When self-hosting the TinaCMS datalayer, you'll need to configure four main components:
### 1. Backend Host
The backend host is where your datalayer API will run. Common options include:
* Next.js API routes
* Vercel Serverless Functions
* Netlify Functions
### 2. Database Adapter
The database adapter determines where your content index is stored. Options include:
* MongoDB
* Vercel KV
* Custom database solutions
Learn more in the [official Database Adapter documentation](https://tina.io/docs/reference/self-hosted/database-adapter/overview).
### 3. Git Provider
The Git provider manages how your content files are stored and retrieved. Options include:
* GitHub
* Custom Git providers
Learn more in the [official Git Provider documentation](https://tina.io/docs/reference/self-hosted/git-provider/overview).
### 4. Auth Provider
The auth provider handles user authentication for your CMS. Options include:
* Auth.js (formerly NextAuth.js)
* Clerk
* TinaCloud auth
* Custom auth solutions
Learn more in the [official Auth Provider documentation](https://tina.io/docs/reference/self-hosted/auth-provider/overview).
## Getting Started
For complete step-by-step instructions on configuring your own datalayer, we recommend following the [official TinaCMS Self-Hosting documentation](https://tina.io/docs/self-hosted/overview).
The official documentation provides detailed examples, code snippets, and best practices for setting up each component of your custom datalayer.

View File

@@ -0,0 +1,59 @@
---
title: What is a Datalayer?
last_edited: '2025-10-27T03:00:50.463Z'
auto_generated: false
---
The Datalayer is a critical component of TinaCMS that acts as the bridge between your content files and the TinaCMS editing interface. It provides:
1. Content Indexing: Indexes your content files to enable fast queries and searching
2. GraphQL API: Generates a GraphQL API based on your content model
3. Authentication: Manages user authentication for the editing interface
4. Storage: Handles content storage and retrieval from your repository
## TinaCloud vs Self-Hosting
TinaCMS offers two approaches for implementing the datalayer:
### TinaCloud (recommended)
TinaCloud is a managed service that handles the Datalayer for you, providing:
* Automatic content indexing
* User authentication and management
* Seamless GitHub integration
* No need to maintain your own infrastructure
This is ideal for most projects and is the quickest way to get started.
A free option also exists for small-scale projects.
### Self-Hosted
The self-hosted option allows you to run the datalayer on your own infrastructure, giving you:
* Complete control over your data
* Custom authentication providers
* Support for custom Git providers
* Integration with your own database
### When to choose Self-Hosted
Consider self-hosting the Datalayer when:
* You need to use a Git provider other than GitHub
* You want to use a custom database for content indexing
* You need a custom authentication system
* You have specific compliance or security requirements
* You have the resources to manage and work with custom hosting infrastructure.
#### Components of a Self-Hosted Datalayer
To self-host the Datalayer, you'll need to configure:
1. Backend Host: A server to run the Datalayer (e.g., Next.js, Vercel Functions)
2. Database Adapter: A database to store and index content (e.g., MongoDB, Vercel KV)
3. Git Provider: A service to store and manage content files (e.g., GitHub, custom Git provider)
4. Auth Provider: A system to authenticate users (e.g., Auth.js, Clerk)
For detailed instructions on setting up a self-hosted Datalayer, see our [Self-Hosting Guide](https://tina.io/docs/self-hosted/overview).

75
content/docs/index.mdx Normal file
View File

@@ -0,0 +1,75 @@
---
title: Welcome to TinaDocs
last_edited: '2025-08-11T08:56:52.364Z'
auto_generated: false
tocIsHidden: true
---
Just deploy and start writing.
TinaDocs is an open source documentation solution built off TinaCMS.
Built for both developers and non-developers.
> No proprietary data store, your content lives entirely in your GitHub repository—versioned, portable, and fully under your control.
## 🧹 Start Fresh (Optional)
Want to start with a clean slate? This template includes example documentation that you can remove with a single command:
```bash
pnpm run cleanup
```
<Callout
body={<>
**IMPORTANT WARNING:**
**This command will PERMANENTLY DELETE all documentation content and only keep this index page.**
* ❌ **If you've already made changes**, running cleanup will DELETE your work
* ✅ **Run cleanup FIRST** if you want a clean slate, then make your changes
* ✅ **Explore examples FIRST** if you want to learn TinaDocs features before deciding
**This action cannot be undone unless you have committed your changes to git.**
</>}
variant="warning"
/>
***
## 📚 Explore the Examples
Or browse through the example documentation to learn about TinaDocs features:
<cardGrid
cards={[
{
title: "What is TinaCMS?",
description: "How TinaCMS works, and whether it's the right fit for you.",
link: "/docs/using-tinacms/what-is-tinacms",
linkText: "Read more"
},
{
title: "Learn the Tools",
description:
"Walk through included features such as API tooling and customization.",
link: "/docs/tinadocs-features/feature-list",
linkText: "See features"
},
{
title: "TinaCloud",
description:
"TinaCloud let's you manage project editors, status and approvals.",
link: "/docs/going-live/tinacloud/what-is-a-datalayer",
linkText: "Read more"
},
{
title: "Get your Docs Live",
description:
"Learn how to take your site live with step-by-step deployment guides.",
link: "/docs/going-live/deploying-your-docs",
linkText: "Go live"
}
]}
/>

View File

@@ -0,0 +1,61 @@
---
id: introduction
seo:
title: "Take Back your Docs \U0001F680"
description: Example Description
title: Showcase
last_edited: '2025-07-15T06:54:30.090Z'
tocIsHidden: false
---
Most documentation kits lock you into certain style patterns and rigid workflows. **This starter package changes that.**
As an open source project using a headless CMS, it gives you full control over both your content and your UI—no templates you cant tweak or structures youre stuck with.
## Built-in Themes
With 6 bespoke themes and an elegant design system, docs are a pleasure to read and write.
![](/tinadocs-stack.png "Themed to perfection")
## Use Cases
Out of the box, TinaDocs has all the tools to suit several common docs needs.
Built with React and Next.js, extend as needed with code.
<accordionBlock
fullWidth={true}
accordionItems={[
{
heading: "Libraries and Packages",
docText:
"Document your package definitions with tailored components, installation guides, and comprehensive reference materials.\n\nPerfect for open source projects and developer tools.\n",
image: "/img/docs-assets/showcase-example-1.png"
},
{
heading: "Tutorials and Learning Material",
docText:
"Interactive code blocks and variety of ways to present information make complex concepts accessible and engaging.\n",
image: "/img/docs-assets/showcase-example-2.png"
},
{
heading: "API Documentation",
docText:
"Auto-generate endpoint documentation from your OpenAPI specification including request/response schemas and example code.\n\nKeep your API docs in sync with your code.\n",
image: "/img/docs-assets/showcase-example-3.png"
},
{
heading: "Internal Documents",
docText:
"Centralize team knowledge with process documentation, on-boarding materials, and internal wikis. Keep everything organized and easily accessible chained to **no proprietary database**, your documents are yours, forever.\n",
image: "/img/docs-assets/showcase-example-4.png"
},
{
heading: "User Guides",
docText:
"Build comprehensive help documentation with searchable content, troubleshooting guides, and clear how-to instructions for your users.\n",
image: "/img/docs-assets/showcase-example-5.png"
}
]}
/>

View File

@@ -0,0 +1,253 @@
---
title: Code Components
last_edited: '2025-07-30T02:30:04.636Z'
auto_generated: false
---
Several components have been implemented to help out with software documentation.
## Code Blocks
For multiline code samples, use triple backticks. You can also specify which language is used in the fenced code block for associated code highlighting.
```javascript
js function greet(name) {
return `Hello, ${name}!`
}
```
### Syntax Highlighting
TinaDocs uses [Shiki](https://shiki.style/) to power code block rendering, because of this you are able to also use [Shiki's Transformer Notation](https://shiki.style/guide/transformers) for special highlighting.
```javascript
console.log('This is a highlighted block') // [!code highlight]
It uses "[!code highlight]" in a comment after the code
```
### Diff Highlighting
```javascript
console.log('This is a negative diff') // [!code --]
console.log('This is a positive diff') // [!code ++]
It uses "[!code --]" or "[!code ++]" in a comment after the code
```
### Focused Highlighting
```javascript
console.log('Junk Code');
console.log('Focused Code'); // [!code focus]
console.log('More hidden code');
It uses "[!code focus]" in a comment after the code
```
## Tabbed Code Blocks
Tabbed code blocks let you present multiple language variants or configurations side-by-side—ideal for showing equivalent code in JavaScript, TypeScript, Bash, or specific queries and responses.
This improves readability and UX for polyglot dev environments.
<codeTabs
tabs={[
{
name: "cURL",
content: "curl<72>-<2D>X<EFBFBD>GET<45>https://jsonplaceholder.typicode.com/posts/1",
language: "shell"
},
{
name: "Response",
content:
'{\n<><6E>"userId":<3A>1,\n<><6E>"id":<3A>1,\n<><6E>"title":<3A>"sunt<6E>aut<75>facere<72>repellat<61>provident<6E>occaecati<74>excepturi<72>optio<69>reprehenderit",\n<><6E>"body":<3A>"quia<69>et<65>suscipit\\nsuscipit<69>recusandae<61>consequuntur<75>expedita<74>et<65>cum..."\n}',
language: "json"
}
]}
initialSelectedIndex={1}
/>
<codeTabs
tabs={[
{
name: "React",
content:
"import<72>React,<2C>{<7B>useState<74>}<7D>from<6F>'react';\n\nfunction<6F>Counter()<29>{\n<><6E>const<73>[count,<2C>setCount]<5D>=<3D>useState(0);\n\n<><6E>return<72>(\n<><6E><EFBFBD><EFBFBD><div>\n<><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><p>Count:<3A>{count}</p>\n<><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><button<6F>onClick={()<29>=><3E>setCount(count<6E>+<2B>1)}>Increment</button>\n<><6E><EFBFBD><EFBFBD></div>\n<><6E>);\n}\n\nexport<72>default<6C>Counter;",
language: "javascript"
},
{
name: "Vue",
content:
"<template>\n<><6E><div>\n<><6E><EFBFBD><EFBFBD><p>Count:<3A>{{<7B>count<6E>}}</p>\n<><6E><EFBFBD><EFBFBD><button<6F>@click=\"increment\">Increment</button>\n<><6E></div>\n</template<74>>\n\n<script<70>setup>\n<><6E>import<72>{ref}<7D>from<6F>'vue';\n\n<><6E>const<73>count<6E>=<3D>ref(0);\n<><6E>const<73>increment<6E>=<3D>()<29>=><3E>count.value++;\n</script>",
language: "javascript"
},
{
name: "Angular",
content:
"import<72>{<7B>Component<6E>}<7D>from<6F>'@angular/core';\n\n@Component({\n<><6E>selector:<3A>'app-counter',\n<><6E>template:<3A>`\n<><6E><EFBFBD><EFBFBD><div>\n<><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><p>Count:<3A>{{<7B>count<6E>}}</p>\n<><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><button<6F>(click)=\"increment()\">Increment</button>\n<><6E><EFBFBD><EFBFBD></div>\n<><6E>`\n})\nexport<72>class<73>CounterComponent<6E>{\n<><6E>count<6E>=<3D>0;\n<><6E>increment()<29>{\n<><6E><EFBFBD><EFBFBD>this.count++;\n<><6E>}\n}",
language: "javascript"
}
]}
initialSelectedIndex={2}
/>
## Recipe Block
Perfect for tutorials, API documentation, or any scenario where you want to guide users through code examples with numbered steps corresponding to specific lines of code.
<recipe
code="import<72>React,<2C>{<7B>useState<74>}<7D>from<6F>'react';
function<6F>Counter()<29>{
<20><>const<73>[count,<2C>setCount]<5D>=<3D>useState(0);
<20><>return<72>(
<20><><EFBFBD><EFBFBD><div>
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><p>Count:<3A>{count}</p>
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><button<6F>onClick={()<29>=><3E>setCount(count<6E>+<2B>1)}>Increment</button>
<20><><EFBFBD><EFBFBD></div>
<20><>);
}
export<72>default<6C>Counter;"
title=""
description=""
instruction={[
{
itemDescription: "Import the useState hook from React",
header: "Imports",
codeLineStart: 1,
codeLineEnd: 1
},
{
itemDescription: "Define a functional component named Counter",
header: "Define Component",
codeLineStart: 3,
codeLineEnd: 3
},
{
header: "Define State",
itemDescription:
"Declare state variable `count` with initial value 0, `setCount` is the updater function",
codeLineStart: 4,
codeLineEnd: 4
},
{
itemDescription: "Return JSX to render the UI",
header: "Render Logic",
codeLineStart: 7,
codeLineEnd: 10
}
]}
/>
## API Route Block
Takes data directly from an OpenAPI spec to populate this component with schema, responses and examples.
<apiReference schemaFile="Swagger-Petstore.json|GET:/user/login" />
## Directory
To convey code architecture or organisation conventions, showing users folder structures helps convey structural ideas that can be tricky to explain textually.
The editing experience is smooth with a tailored drag-and-drop interface.
<fileStructure
fileStructure={[
{ id: "zm0xmlefo", name: "Folder A", type: "folder", parentId: null },
{ id: "d7bw8ns62", name: "File A.txt", type: "file", parentId: "zm0xmlefo" },
{
id: "xaiajws95",
name: "Subfolder A",
type: "folder",
parentId: "zm0xmlefo"
},
{ id: "xv64qw84k", name: "Folder B", type: "folder", parentId: null },
{ id: "beqrd40u0", name: "Subfolder", type: "folder", parentId: "xv64qw84k" },
{ id: "yijaqtwhg", name: "File B.txt", type: "file", parentId: "beqrd40u0" },
{ id: "hfv48qb0z", name: "Home Route File.txt", type: "file", parentId: null }
]}
caption="An example file structure"
/>
## Mermaid
TinaDocs supports [Mermaid.js](https://mermaid.js.org/), to define and render diagrams directly inside Markdown content.
This lets you to add flowcharts, sequence diagrams, Gantt charts, and more.
The diagram shares the code block node in the editor, but lets you preview what the diagram will look like.
<Callout
body={<>
Mermaid Rendering is **client-side** only, so server-side previews wont show diagrams
</>}
variant="warning"
/>
### Flowcharts
```mermaid
%% This won't render without implementing a rendering engine (e.g. mermaid on npm)
flowchart TD
id1(this is an example flow diagram)
--> id3(Click the top button to preview the changes)
```
### Sequence Diagrams
```mermaid
%% This won't render without implementing a rendering engine (e.g. mermaid on npm)
sequenceDiagram
participant Client
participant API
participant DB
Client->>API: GET /data
API->>DB: Query data
DB-->>API: Result
API-->>Client: Response
```
### State Diagram
```mermaid
stateDiagram-v2
[*] --> Waiting
Waiting --> RequestSent: Client calls API
RequestSent --> Complete: API sends response
Complete --> [*]
```
### Class Diagram
```mermaid
classDiagram
class Client {
+requestData()
}
class API {
+handleRequest()
+queryDB()
}
class DB {
+runQuery()
}
Client --> API : calls
API --> DB : queries
```
### Pie Chart
```mermaid
pie
title API Resource Usage
"Client Requests" : 30
"API Processing" : 40
"DB Query Time" : 30
```

View File

@@ -0,0 +1,47 @@
---
title: Custom Components
last_edited: '2025-07-15T06:02:42.242Z'
---
You can add you own components as an embed, similar to the text and code components.
**This flexibility is one of the key reasons to use TinaCMS and this package.**
## The Technical Side of Component Rendering
All content written in the body of your documentation is stored as the rich-text data-type.
This data-type is broken down into a hierarchical abstract syntax tree (AST).
This data can be parsed into a `<TinaMarkdown>` object to populate it with data:
```javascript
import MarkdownComponentMapping from "@/markdown-component-mapping";
<TinaMarkdown
content={documentionData?.body} // [!code highlight]
components={MarkdownComponentMapping}
/>
```
After this, the `MarkdownComponentMapping` is used to parse each object from the AST data into their corresponding React components based on node type, allowing you to override default rendering behaviour for elements like headings, links, code blocks, and more.
### Example
```javascript
import { TinaMarkdown } from 'tinacms/dist/rich-text'
export const CustomMarkdown = ({ content }) => {
return (
<TinaMarkdown
content={content}
components={{
h2: (props) => <h2 className="text-2xl text-blue-600" {...props} />,
a: (props) => <a className="underline text-red-500" {...props} />,
code_block: (props) => <pre className="bg-black text-white p-2">{props.children}</pre>,
}}
/>
)
}
```

View File

@@ -0,0 +1,87 @@
---
title: Feature List
last_edited: '2025-07-31T06:10:36.054Z'
auto_generated: false
---
This page acts as an index for the TinaDocs feature-set.
Have an awesome idea for a feature we should add, or keen to contribute? [Join our Discord](https://discord.com/invite/zumN63Ybpf)
## Components
TinaCMS is a bare-bones tool with no pre-built page elements.
TinaDocs brings a number of key page elements (components) needed for comprehensive documentation, especially for software projects.
They can all be modified or extended upon.
> Some of these components were built in React and used as .mdx components.
<cardGrid
cards={[
{
title: "Markdown",
description:
"Standard markdown elements such as lists, tables, headings have all been designed to match the overall theme.",
link: "/docs/tinadocs-features/markdown-elements",
linkText: "Read more"
},
{
title: "Accordions, Cards, Callouts",
description:
"Accordions, callouts and cards let you break up and organise information more easily.",
link: "/docs/tinadocs-features/text-components",
linkText: "Read more"
},
{
title: "Code Elements",
description:
"Convey technical details clearly with powerful code blocks, mermaid diagram integration, tabbed code blocks and recipe blocks. 🧑‍💻",
link: "/docs/tinadocs-features/code-components",
linkText: "Read more"
},
{
title: "Modifying Components",
description:
"Read more about component rendering in this project from a technical perspective.",
link: "/docs/tinadocs-features/custom-components",
linkText: "Learn more"
}
]}
/>
## Website Theming and Configuration
We've included a number of ways to configure or manage your document site more generally.
<cardGrid
cards={[
{
title: "OpenAPI Doc Generation",
description:
"Generate documents based on a provided OpenAPI spec, and re-sync on demand. 🔌",
link: "/docs/tinadocs-features/openapi-spec-docs",
linkText: "Read more"
},
{
title: "Styling",
description:
"The overall look and feel of these documents can be configured manually (in code) or with built-in options.",
link: "/docs/tinadocs-features/styling",
linkText: "Read more"
},
{
title: "Search Engine Optimization",
description: "Manage your SEO on a page-by-page basis.",
link: "/docs/tinadocs-features/search-engine-optimization-seo",
linkText: "Read more"
},
{
title: "Redirect Management",
description: "Define NextJS redirects from your editing window.",
link: "/docs/tinadocs-features/redirect-management",
linkText: "Read more"
}
]}
/>

View File

@@ -0,0 +1,76 @@
---
title: Markdown Elements
last_edited: '2025-07-15T05:00:20.940Z'
---
Markdown natively contains some key elements including images, tables, lists, etc.
These have been styled when parsed into your page to match the overall theme.
## Text Formatting
With the base editor we can add [links](/), **bold**, *italic* and ~~strikethrough~~ formatting.
### Inline Code
Using a singular backtick we can wrap small bits of `code`.
## Lists
Lists in markdown are indicated with `*`, `-` or `1.`.
We can order our lists:
1. First Item
2. Second Item
Our keep them unordered:
* First Item
* Second Item
## Block-quotes
Proceed a line with > to create a block-quote.
> Block-quotes are useful to give tips or notes.
## Tables
The markdown syntax for tables is as below.
```
| Feature | Free Plan | Pro Plan |
| -----------| ----------| ---------|
| Hosting | ✓ | ✓ |
| API Access | ✗ | ✓ |
| Support | Email | Priority |
```
However, TinaCMS' extension of PlateJS allows you to skip manual styling and created tables via the toolbar.
| Feature | TinaCMS | Others |
| ----------- | ---------------- | ----------------- |
| Cool Team | ✓ | ✗ |
| Open Source | ✓ | ✗ |
| Mascot | Super cool llama | Nothing, probably |
## Images
When editing, click on the picture icon in the toolbar and you'll be able to upload, select, and pick from images in your repositories media library.
Click into the image to manage alt text and captions.
Users can click on the image in your documentation to view a full-screen version for accessibility.
![](/img/rico-replacement.jpg "This is an example llama")
### Videos
You can also embed YouTube videos in your documentation site.
Click on the three dots in your tool-bar, click 'Embed' and select YouTube video.
You can click into these embed objects to modify their fields (i.e Video URL, Captions etc)
<youtube embedSrc="https://www.youtube.com/embed/CsCQS7HIBv0?si=os9ona92O2VMOl-V" caption="Seth goes over the basics of using TinaCMS" minutes="2" />

View File

@@ -0,0 +1,42 @@
---
title: OpenAPI Spec Docs
last_edited: '2025-07-15T07:30:50.546Z'
---
TinaDocs includes features to auto-generate docs pages based on an OpenAPI spec.
You can also choose to use the same components to create API pages manually.
## Upload a Specification
There's a unique collection for storing OpenAPI specifications, and they can be managed independently.
![](</img/docs-assets/api-spec-upload.png> "Uploading a spec via the API Schema collection")
## Using Specification Data
You can read from the specification inline via the API Reference component or generate sections of the nav-bar based on available routes.
### Inline
Select a schema from the dropdown, and all routes will be shown you can further select a singular route to be used for the component.
![](</img/docs-assets/api-endpoint-selection.png> "Selecting a specific endpoint from the API Reference component")
### Page Generation
You can generate entire directories to match your spec marked and colour coded based on route.
1. Create a Tab with the “API Tab” Template.
2. Create a Supermenu Group with the “Group of API References” Template.
3. Choose the spec to be used, and check endpoints to be included.
4. Press save for generation.
![](</img/docs-assets/menu-generation-from-spec.png> "Generating a menu group and pages from a specification")
<Callout
body={<>
If you have any “Group of API References” with checked options in the Navbar settings, saving will regenerate the documents.
</>}
variant="warning"
/>

View File

@@ -0,0 +1,61 @@
---
title: Path Management
last_edited: '2025-07-30T04:27:09.275Z'
auto_generated: false
---
TinaDocs utilizes **NextJS** to create your sitemap at build time.
By default, the relative URL is controlled by the filepath of each document, with one special case for the home route.
<Callout
body={<>
With access to the code, static builds can be modified to select the path based on other factors, such as the navigation bar.
This is controlled in `/src/app/docs/[...slug]/page.tsx`.
</>}
variant="idea"
/>
## Choosing Paths
It's easiest to choose the filepath for a document at time of creation. Select “Add File” in the Collections menu from the directory where you want your new file located.
See the simple example below.
<fileStructure
fileStructure={[
{ id: "c1sbhudkd", name: "content", type: "folder", parentId: null },
{ id: "zgmygdri2", name: "docs", type: "folder", parentId: "c1sbhudkd" },
{ id: "zualtmn8t", name: "file-a.mdx", type: "file", parentId: "zgmygdri2" },
{
id: "hs6l2trxy",
name: "subsection",
type: "folder",
parentId: "zgmygdri2"
},
{ id: "0z5fdtnn4", name: "file-b.mdx", type: "file", parentId: "hs6l2trxy" },
{ id: "lm7t4d4p6", name: "src", type: "folder", parentId: null },
{ id: "qgv8bk8n4", name: "tina", type: "folder", parentId: null },
{ id: "tmc6ek24f", name: "package.json", type: "file", parentId: null }
]}
caption="The TinaDocs file structure with example content"
/>
As per the above (note that `.mdx` extensions are automatically stripped from URLs)…
* the document `file-a.mdx` is accessible at `/docs/file-a`
* the document `file-b.mdx` is accessible at `/docs/subsection/file-b`
<Callout
body={<>
You can also have multiple files with the same name in different directories.
</>}
variant="info"
/>
## The Home Route
The file at `/content/docs/index.mdx` is handled independently and serves as the landing page.
This will be statically built at `/docs` (home route) of your project.

View File

@@ -0,0 +1,49 @@
---
seo:
title: Managing Redirects with TinaDocs
description: Learn how to set up and manage redirects using TinaDocs and TinaCMS.
title: Redirect Managment
last_edited: '2025-07-15T06:53:56.004Z'
---
TinaDocs is built on top of **NextJS**, which supports static and dynamic redirects through the `next.config.js` file located at the root of your project.
This allows for seamless integration of redirect rules during the build process, ensuring that your routes behave as expected across environments.
## Setting Up Your Redirects
To update or add your redirects using the TinaCMS admin interface, follow these steps:
1. Navigate to the \`/admin\` route of your site.
2. In the sidebar, click on **Settings**.
3. Scroll down to the bottom of the Settings page.
4. Look for the **Redirects** section.
5. Click **+ Add New** to create a new redirect, or click an existing one to modify it.
![](</img/docs-assets/redirect-editor-interface.png> "Adding a redirect via the editor")
Each redirect entry typically includes:
* **Source Path** (from)
* **Destination Path** (to)
* **Permanent** (boolean flag for 301 vs 302)
* **Conditions** (optional advanced rules)
### Code-Defined Redirects
Here's what a redirect entry might look like in `next.config.js` if manually defined:
```javascript
// next.config.js
module.exports = {
async redirects() {
return \[
{
source: '/old-route',
destination: '/new-route',
permanent: true,
},
];
},
};
```

View File

@@ -0,0 +1,27 @@
---
seo:
title: SEO
description: Goes over the SEO Capabilities of TinaDocs
canonicalUrl: https://tina.io/tinadocs/docs/tinadocs-features/search-engine-optimization-seo
ogImage: /og/tina-og.png
title: Search Engine Optimization (SEO)
last_edited: '2025-07-16T00:55:37.712Z'
---
TinaDocs gives you full control over your sites metadata, structure, and performance—ensuring your documentation is optimized for search engines and shareable across platforms.
TinaDocs is built with **NextJS**, meaning you get SEO fundamentals out of the box:
* Server-side rendering or static generation
* Fast page loads with optimized bundle splitting
* Custom `<head>` support per page
## Updating Page SEO
To modify the SEO values on each page:
1. Navigate to the admin route
2. For each page click 'SEO Values' on the top of the editor bar
3. Insert respective SEO values
![](</img/docs-assets/seo-configuration.png>)

View File

@@ -0,0 +1,114 @@
---
title: Styling
last_edited: '2025-10-27T09:05:25.584Z'
auto_generated: false
---
TinaDocs is opinionated when it comes to the style of its documentation. You can change this as you see fit.
## Colours and Theming
There are 6 built colour pre-sets.
These are based on the [radix design system](https://www.radix-ui.com/colors) colours.
![](/img/docs-assets/choose-starter-screen.png "Choose your starter")
You can test these out locally, or configure which theme is selected via the Tina settings.
### Adding a New Theme
We recommend modifying an existing theme rather than creating new theme options.
You can find this configuration in `src/styles/global.css`
You can specify a new theme in addition to those shown, you'll need to update…
* The Tina collection with the new option at `tina/collections/settings.tsx`
* The theme selector, `src/components/ui/theme-selector.tsx`
* The styles file with the new theme, at `src/styles/global.css`
> 💡 **For detailed instructions on creating custom themes**, including step-by-step guides, color guidelines, and examples, see the [Custom Theming section in the README](https://github.com/tinacms/tina-docs/blob/main/README.md).
## Typography
TinaDocs comes with a flexible, clean typographic system.
### Writing with Markdown
The data behind TinaCMS is markdown, so some concepts such as text colour and font size don't exist natively. All editing is done through a rich-text editor powered by [Plate](https://platejs.org/), that still gives you a word-like editing experience.
Everything transforms into markdown behind the scenes.
* Titles use standard Markdown syntax (#).
* You can change font size via headling levels in the toolbar.
* Inline formatting maps 1:1 with Markdown.
* Empty lines aren't available, if this is a problem either modify the `<hr>` element in the markdown mapping to be transparent, or add margins where necessary.
<Callout
body={<>
Markdown forces you to certain conventions, which creates content consistency.
</>}
variant="info"
/>
### Base Style Sizing
TinaDocs uses semantic HTML tags styled with TailwindCSS utility classes to maintain hierarchical sizing.
| Tag | Size | Use Case |
| -------- | --------------- | ------------------------ |
| h1 | 36px (2.25rem) | Page titles |
| h2 | 30px (1.875rem) | Section headings |
| h3 | 24px (1.5rem) | Subsections |
| h4 | 20px (1.25rem) | Minor Subsections |
| h5 | 18px (1.125rem) | Fine-grained Subsections |
| p | 16px (1rem) | Body Text |
| li/ul/ol | 16px (1rem) | Lists |
| code | 16px (1rem) | Inline code |
Tina uses [Tailwind CSS](https://tailwindcss.com/) to style components, giving you a powerful and flexible way to customize your site's appearance.
## Favicon
A favicon is the small icon that appears in your browser tab, bookmarks, and on mobile home screens.
TinaDocs supports custom favicons so you can brand your documentation site to match your product.
1. Open the codebase of your new site
2. Navigate to the public/ directory
3. Delete the existing favicon.ico
4. Insert your own logo image into the public/ directory
5. Rename it to favicon.xxx
<Callout
body={<>
Replace 'xxx' with your file type. Supported file types are .ico, .png and .svg
</>}
variant="warning"
/>
## Custom Styling
Styling for components or the overall site requires modifying the code, but is straightforward.
### Using Tailwind CSS
You can customize your site's styling by:
1. Modifying your `tailwind.config.js` file to update colors, fonts, spacing, and other design tokens
2. Applying Tailwind utility classes directly to components
3. Creating custom CSS in your project that extends Tailwind's capabilities
For a complete guide to Tailwind's capabilities, refer to the [official Tailwind CSS documentation](https://tailwindcss.com/docs).
### Component-Specific Styling
You can customize the appearance of specific Tina components the tailwind in the `/app` router and embeds (`/components/tina-markdown`) directories can be updated to match your colour scheme and UI standards.
TinaCMS itself is only coupled to the props defining data for components.
```jsx
// Example: Customizing a Tina component
<TinaComponent className="bg-blue-500 text-white rounded-lg shadow-md" />
```

View File

@@ -0,0 +1,130 @@
---
title: Text Components
last_edited: '2025-10-27T10:01:36.180Z'
auto_generated: false
---
Three embedded components accordions, callouts and cards are available out of the box.
<Callout
body={<>
We can embed non-standard features into our markdown with the power of [MDX](https://mdxjs.com/).
</>}
variant="idea"
/>
## Using Embedded Components
You can see and add an Embed from the rich-text editor toolbar.
![](/img/docs-assets/embed-toolbar-access.png "Access the embed tab of the toolbar.")
## Cards
Cards can be used to showcase information or as links.
They highlight on hover based on the theme.
<cardGrid
cards={[
{
title: "Pine",
description: "Linked card.",
link: "https://www.google.com",
linkText: "Search now"
},
{
title: "Indigo",
description: "This card doesn't link anywhere.",
link: "",
linkText: ""
},
{ title: "Blossom", description: "🌸", link: "", linkText: "" },
{ title: "Lake", description: "🏞️", link: "", linkText: "Search now" }
]}
/>
## Accordions
Collapsible content areas.
This is ideal for FAQs, advanced explanations, or progressive disclosure UI patterns.
### Multi-accordion
You can block all of your accordions with the option to have them as full-width or half-width.
<accordionBlock
fullWidth={true}
accordionItems={[
{
heading: "Click to expand",
docText: "Default Text. Edit me!\n",
image: "/img/rico-replacement.jpg"
},
{
heading: "Click to expand",
docText: "Default Text. Edit me!\n",
image: "/img/rico-replacement.jpg"
}
]}
/>
### Individual Accordion
Accordions can also exist standalone.
<accordion
heading="Click to expand"
docText={<>
Default Text. Edit me!
</>}
image="/img/rico-replacement.jpg"
fullWidth={true}
/>
## Callouts
Callouts are similar to block-quotes, to add a note or indication outside the normal content flow.
<Callout
body={<>
Multiple variants exist to convey different messaging.
</>}
variant="success"
/>
<Callout
body={<>
Some callouts indicate potential issues to users.
</>}
variant="warning"
/>
<Callout
body={<>
Some callouts indicate existing issues, or negative examples.
</>}
variant="error"
/>
<Callout
body={<>
Some are just for a note or tip.
</>}
variant="idea"
/>
<Callout
body={<>
Security related callouts exist too 👮.
</>}
variant="lock"
/>
<Callout
body={<>
…or some other specific options!
</>}
variant="api"
/>

View File

@@ -0,0 +1,47 @@
---
title: How Does TinaCMS Work?
last_edited: '2025-07-12T22:32:27.382Z'
---
TinaCMS is a feature-rich and somewhat complex tool.
If you just want a docs solution, feel free to skip over this chapter. If you yearn for knowledge, read on….
## The TinaCMS Frontend
The TinaCMS front-end is 2 fold:
1. An editing portal that runs on the same port as your website, by default at the `/admin/index.html` route.
2. Your content model definition (we call it your schema) and other configuration… this control what fields you get when editing your website.
![](</img/docs-assets/tinacms-admin-portal.png> "Access the content portal on the /admin/index.html route of your website")
## The TinaCMS Backend
The TinaCMS Backend is your GraphQL content API.
Think of this as a database and server that indexes your flat Git files for efficient fetching and exposes the endpoints.
![](</img/docs-assets/tinacms-data-flow.png> "Data moves from GitHub to your website via the TinaCMS Backend")
This set-up can be achieved with TinaCloud a paid offering from the TinaCMS team, or by using your own database.
> During local development, a local version of the server is running that allows for the same editing experience offline.
## Learn More About TinaCMS
This kit won't go deep into TinaCMS other than basic usage [their docs](https://tina.io/docs) have more info.
Some useful links are highlighted below…
### The Beginner Series
There's a series of intro tutorials called [the Beginner Series](https://tina.io/docs/beginner-tutorials/tutorial-overview) that teach you to integrate a website with the CMS, giving you a full website builder feature set (dragging blocks around a page, live previews and click to edit functionality).
### Usage Docs
Some useful reference docs include…
* What is [markdown vs mdx ](https://tina.io/docs/editing/markdown)and what is the supported [markdown spec](https://tina.io/docs/reference/markdown-spec)
* Setup guide for [AI powered auto-translations and internationalization](https://tina.io/docs/guides/internationalization) (if useful for your project)
* Media options, including [repo-based (Git) media](https://tina.io/docs/reference/media/repo-based) and [external media stores](https://tina.io/docs/reference/media/external/authentication)

View File

@@ -0,0 +1,69 @@
---
title: Usage Developers
last_edited: '2025-07-15T00:16:15.119Z'
---
<Callout
body={<>
We recommend reading the previous page on usage for editors before moving onto this one. This page is focused on the local development experience.
</>}
variant="idea"
/>
The TinaCMS editor has two modes:
1. **Local Development Mode**: When running your site locally for development
2. **Production Mode**: When using the deployed version of your site
In local development mode, changes are saved directly to files in your local repository.
In production mode (with TinaCloud), changes are committed directly to your Git repository.
## Local Mode
The TinaCMS editor is available at the `/admin` route of your site (for example, `http://localhost:3000/admin`).
Run the below command from the terminal in your project directory to boot up the application.
```shell
pnpm dev
```
This will run the TinaCMS local server as well as the NextJS front-end.
The editing UI is the same as in production, but you can safely test any model or content changes that are out of the ordinary.
### ⚠️ The **tina-lock.json** File
The `tina-lock.json` must be **checked into source control and pushed to your repo**.
It contains a compiled schema used to resolve content documents, and should be updated after any local content or schema changes.
Run `pnpm dev` locally to trigger an update to this file.
## Testing Production Mode
You can also test a production instance of your application, hooked up to TinaCloud.
This may be useful in a pinch to pick up hard-to-find issues.
1. Update your local .env file with:
1. `TINA_TOKEN` from TinaCloud
2. `NEXT_PUBLIC_TINA_CLIENT_ID` from TinaCloud
3. `NEXT_PUBLIC_TINA_BRANCH` if using branch based editing
4. `NEXT_PUBLIC_SITE_URL` to `localhost:3000`
2. Run the build and start scripts.
```shell
pnpm build && pnpm start
```
## Next Steps
Once you're familiar with the basic editing interface, you might want to:
1. Customize your content schema
2. Create custom field components
3. Configure advanced validation
For more details on the editing experience, check out the [official TinaCMS documentation](https://tina.io/docs/using-tina-editor).

View File

@@ -0,0 +1,104 @@
---
title: Usage Editors
last_edited: '2025-07-15T00:15:32.898Z'
---
> These instructions assume you've already deployed your site if you created it with the TinaCloud QuickStart, this has all been done for you.
Let's get to grips with the **no-code editing workflow** in production.
<Callout
body={<>
Prefer to watch a video? Follow the one provided.
</>}
variant="idea"
/>
<youtube embedSrc="https://www.youtube.com/embed/CsCQS7HIBv0?si=os9ona92O2VMOl-V" caption="Seth goes over the basics of using TinaCMS" minutes="2" />
## First Steps…
1. You'll need a GitHub account, if you don't have one create one and come back.
2. Add your account to the project in TinaCloud (or ask your admins to add you).
## Making Edits
1. Go to the [/admin](/admin#/~/docs/tinadocs-and-tinacms/editing-content) route of your website
2. Change the content in one of the fields and watch your site preview update in real time. This is called **visual editing**.
3. Press save.
<Callout
body={<>
This will automatically trigger an update to your site, unless you have [editorial workflow](https://tina.io/editorial-workflow) features enabled (giving teams a Git based approval workflow).
</>}
variant="warning"
/>
> **Note:** these kinds of updates shouldn't create any downtime, depending on your deployment pipeline.
## Editing a Specific Page
### Collections Menu
You can either look through all your pages via the **collections menu**, or turn on edit mode for a specific page.
To go to the collections menu, go to your [/admin](/admin) route, then open the sidebar and follow the below graphic.
![](</img/docs-assets/collections-menu-access.png> "Accessing a specific page via the collections menu")
Clicking on any of the above documents will take you to the visual editor for that page.
### By URL
Alternatively, you can access the editor for a page you're on by updating the url.
Between the **domain** and **the path** of the page you want to edit, add `/admin/#/~/` to take you to the live editor for that page.![](</img/docs-assets/edit-route-navigation.png> "Going to the edit route on a particular URL")
## Adding a New Page
To create a new doc, go to the Docs collection and click “Add File”.
Folders can be organised as you see fit, and this file path controls the url of that document.
> For example, a document, `A.mdx` in the folder `/B` can be accessed at `DOMAIN/docs/B/A`.
![](</img/docs-assets/add-new-page-collections.png> "Adding a new page via the collections menu")
<Callout
body={<>
All documents will be exposed, regardless of whether they're included in the navigation bar or not.
</>}
variant="warning"
/>
## Managing the Navigation Bar
Docs are organised in tabs and then menus within each tab.
You can manage this from the Navigation Bar collection.
### Adding a New Tab
When adding a new tab, there are two options either an API tab and a Docs tab.
* **Docs Tab** allows you to reference documents in a tree structure to create a navigation structure.
* **API Tab** includes the option to add auto-generated documents from an OpenAPI spec to your sidebar, in addition to regular documents.
<Callout
body={<>
Currently, saving the Navigation Bar collection will re-generate these API Tab documents.
</>}
variant="warning"
/>
![](/img/docs-assets/navigation-new-tab.png "Adding a new tab via the Navigation Bar collection")
### Menu Structure Changes
The menu structure is controlled as an tree-like object inside each Tab.
At each level of the menu, you can choose between a sub-menu or a document file to create the nested navigation structure.
![](</img/docs-assets/menu-tree-structure.png> "Inside the Tab object exists your menu tree structure")
Documents are labelled in the sidebar based on their title field, and menu groups can be given custom names.

View File

@@ -0,0 +1,35 @@
---
title: What is TinaCMS?
last_edited: '2025-07-11T07:33:46.510Z'
---
[TinaCMS](https://tina.io) is a Headless Git-backed CMS for developers.
<Callout
body={<>
A headless CMS is a tool that stores content and sends it to any website or app through an API, without controlling the frontend implementation.
</>}
variant="info"
/>
It lets you build a custom content editing portal using React, with changes stored as flat files and committed straight to Git.
This exists separately to your website, which hooks into TinaCMS and pulls that content from Git (see below).
![](</img/landing-assets/tinacms-github-integration.jpg> "Keep everything in GitHub")
For more details, check out the TinaCMS [documentation](https://tina.io/docs).
If you have further questions, feel free to join the TinaCMS community on [Discord](https://discord.com/invite/zumN63Ybpf).
## Why TinaCMS?
This may or may not be the right fit for your project.
| Feature | Key Benefit |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Flat-file based data storage | Flat files make migrations and data storage straightforward with markdown and mdx as common content file-types. |
| GitHub Workflow | Favors teams that want to or already have GitHub based projects and allows code and documentation to sit side by side in a mono-repo. |
| Headless | You can change your content model and website independently. |
| Custom Editing Interface | You can tailor your editing panel with custom components for custom validation, data, etc. |
| **Open Source** | **Modify the project as you need, or help maintain it 🫶** |

View File

@@ -0,0 +1,166 @@
{
"lightModeLogo": "/svg/tina-icon-orange.svg",
"darkModeLogo": "/svg/tina-icon-orange.svg",
"tabs": [
{
"title": "Docs",
"supermenuGroup": [
{
"title": "Introduction",
"items": [
{
"slug": "content/docs/index.mdx",
"_template": "item"
},
{
"slug": "content/docs/introduction/showcase.mdx",
"_template": "item"
}
]
},
{
"title": "Using TinaCMS",
"items": [
{
"slug": "content/docs/using-tinacms/what-is-tinacms.mdx",
"_template": "item"
},
{
"slug": "content/docs/using-tinacms/how-does-tinacms-work.mdx",
"_template": "item"
},
{
"slug": "content/docs/using-tinacms/usage-editors.mdx",
"_template": "item"
},
{
"slug": "content/docs/using-tinacms/usage-developers.mdx",
"_template": "item"
}
]
},
{
"title": "TinaDocs Features",
"items": [
{
"slug": "content/docs/tinadocs-features/feature-list.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/styling.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/markdown-elements.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/text-components.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/code-components.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/custom-components.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/openapi-spec-docs.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/search-engine-optimization-seo.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/path-management.mdx",
"_template": "item"
},
{
"slug": "content/docs/tinadocs-features/redirect-management.mdx",
"_template": "item"
}
]
},
{
"title": "Going Live",
"items": [
{
"title": "TinaCloud",
"items": [
{
"slug": "content/docs/going-live/tinacloud/what-is-a-datalayer.mdx",
"_template": "item"
},
{
"slug": "content/docs/going-live/tinacloud/configuring-tinacloud.mdx",
"_template": "item"
},
{
"slug": "content/docs/going-live/tinacloud/custom-datalayer.mdx",
"_template": "item"
}
],
"_template": "items"
},
{
"slug": "content/docs/going-live/deploying-your-docs.mdx",
"_template": "item"
}
]
}
],
"_template": "docsTab"
},
{
"title": "API",
"supermenuGroup": [
{
"apiGroup": "{\"schema\":\"Swagger-Petstore.json\",\"tag\":\"user\",\"endpoints\":[{\"id\":\"POST:/user\",\"label\":\"POST /user - Create user\",\"method\":\"POST\",\"path\":\"/user\",\"summary\":\"Create user\",\"description\":\"This can only be done by the logged in user.\"},{\"id\":\"GET:/user/logout\",\"label\":\"GET /user/logout - Logs out current logged in user session\",\"method\":\"GET\",\"path\":\"/user/logout\",\"summary\":\"Logs out current logged in user session\",\"description\":\"\"}]}",
"_template": "groupOfApiReferences"
},
{
"title": "Introduction",
"items": [
{
"slug": "content/docs/api-documentation/overview.mdx",
"_template": "item"
},
{
"slug": "content/docs/examples/pet-store-all-routes.mdx",
"_template": "item"
},
{
"slug": "content/docs/examples/library-api-example.mdx",
"_template": "item"
},
{
"slug": "content/docs/examples/internal-document-example.mdx",
"_template": "item"
}
],
"_template": "documentSubMenu"
},
{
"apiGroup": "{\"schema\":\"Swagger-Petstore.json\",\"tag\":\"pet\",\"endpoints\":[{\"id\":\"POST:/pet/{petId}/uploadImage\",\"label\":\"POST /pet/{petId}/uploadImage - uploads an image\",\"method\":\"POST\",\"path\":\"/pet/{petId}/uploadImage\",\"summary\":\"uploads an image\",\"description\":\"\"},{\"id\":\"PUT:/pet\",\"label\":\"PUT /pet - Update an existing pet\",\"method\":\"PUT\",\"path\":\"/pet\",\"summary\":\"Update an existing pet\",\"description\":\"\"},{\"id\":\"GET:/pet/findByTags\",\"label\":\"GET /pet/findByTags - Finds Pets by tags\",\"method\":\"GET\",\"path\":\"/pet/findByTags\",\"summary\":\"Finds Pets by tags\",\"description\":\"Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.\"},{\"id\":\"POST:/pet/{petId}\",\"label\":\"POST /pet/{petId} - Updates a pet in the store with form data\",\"method\":\"POST\",\"path\":\"/pet/{petId}\",\"summary\":\"Updates a pet in the store with form data\",\"description\":\"\"},{\"id\":\"POST:/pet\",\"label\":\"POST /pet - Add a new pet to the store\",\"method\":\"POST\",\"path\":\"/pet\",\"summary\":\"Add a new pet to the store\",\"description\":\"\"},{\"id\":\"GET:/pet/findByStatus\",\"label\":\"GET /pet/findByStatus - Finds Pets by status\",\"method\":\"GET\",\"path\":\"/pet/findByStatus\",\"summary\":\"Finds Pets by status\",\"description\":\"Multiple status values can be provided with comma separated strings\"}]}",
"_template": "groupOfApiReferences"
}
],
"_template": "apiTab"
}
],
"ctaButtons": {
"button1": {
"label": "TinaCMS Docs",
"link": "https://tina.io/docs",
"variant": "primary-outline"
},
"button2": {
"label": "TinaCloud",
"link": "https://app.tina.io",
"variant": "primary-background"
}
}
}

View File

@@ -0,0 +1,28 @@
{
"selectedTheme": "default",
"redirects": [
{
"source": "/",
"destination": "/docs",
"permanent": true
}
],
"title": "Tina",
"description": "TinaCMS is a fully open-source headless CMS that supports Git",
"seoDefaultTitle": "TinaCMS Headless CMS with GitHub & Markdown Support",
"publisher": "TinaCMS",
"applicationName": "TinaCMS",
"siteUrl": "https://tina.io",
"roadmapUrl": "https://tina.io/roadmap/",
"licenseUrl": "https://github.com/tinacms/tinacms/blob/master/LICENSE",
"keywords": "nextjs, react, cms, next, tina, markdown, git, open-source, headless",
"docsHomepage": "/docs/index",
"autoApiTitles": true,
"defaultOGImage": "/og/tina-og.png",
"social": {
"twitterHandle": "@tinacms",
"twitter": "https://twitter.com/tinacms",
"github": "https://github.com/tinacms/tinacms",
"forum": "https://tina.io/community"
}
}

17
content/siteConfig.json Normal file
View File

@@ -0,0 +1,17 @@
{
"title": "Tina",
"sidebarTitle": "Tina",
"seoDefaultTitle": "TinaCMS Headless CMS with GitHub & Markdown Support",
"description": "TinaCMS is a fully open-source headless CMS that supports Git",
"siteUrl": "https://tina.io",
"roadmapUrl": "https://tina.io/roadmap/",
"licenseUrl": "https://github.com/tinacms/tinacms/blob/master/LICENSE",
"keywords": "nextjs, react, cms, next, tina, markdown, git, open-source, headless",
"docsHomepage": "/docs/index",
"social": {
"twitterHandle": "@tinacms",
"twitter": "https://twitter.com/tinacms",
"github": "https://github.com/tinacms/tinacms",
"forum": "https://tina.io/community"
}
}

5
next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

19
next-sitemap.config.js Normal file
View File

@@ -0,0 +1,19 @@
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: `${process.env.NEXT_PUBLIC_SITE_URL}${process.env.NEXT_PUBLIC_BASE_PATH || ''}`,
changefreq: "daily",
priority: 0.7,
sitemapSize: 5000,
generateRobotsTxt: true,
output: "standalone",
outDir: "public/doc",
generateIndexSitemap: false,
robotsTxtOptions: {
policies: [
{
userAgent: "*",
allow: "/",
},
],
},
};

102
next.config.js Normal file
View File

@@ -0,0 +1,102 @@
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
const redirects = require("./content/settings/config.json")?.redirects || [];
/** @type {import('next').NextConfig} */
const isStatic = process.env.EXPORT_MODE === "static";
const basePath = process.env.NEXT_PUBLIC_BASE_PATH;
const assetPrefix = process.env.NEXT_PUBLIC_ASSET_PREFIX || basePath;
const extraConfig = {};
if (isStatic) {
extraConfig.output = "export";
extraConfig.trailingSlash = true;
extraConfig.skipTrailingSlashRedirect = true;
}
module.exports = {
...extraConfig,
basePath,
assetPrefix,
images: {
...(assetPrefix ? { path: `${assetPrefix}/_next/image` } : {}),
remotePatterns: [
{
protocol: "https",
hostname: "assets.tina.io",
port: "",
},
],
},
outputFileTracingIncludes: {
"/api/**/*": [],
},
outputFileTracingExcludes: {
"/api/**/*": [
".next/cache/**/*",
"node_modules/@swc/core-linux-x64-gnu",
"node_modules/@swc/core-linux-x64-musl",
"node_modules/@esbuild/",
"node_modules/webpack",
"node_modules/terser",
".git/**/*",
"public/**/*",
],
},
async rewrites() {
return [
{
source: "/admin",
destination: "/admin/index.html",
},
];
},
async redirects() {
return redirects.map((redirect) => ({
source: redirect.source,
destination: redirect.destination,
permanent: redirect.permanent,
}));
},
turbopack: {
resolveExtensions: [".mdx", ".tsx", ".ts", ".jsx", ".js", ".mjs", ".json"],
// Add this rule to handle SVG as React components for Local Development
rules: {
"*.svg": {
loaders: ["@svgr/webpack"],
as: "*.js",
},
},
},
webpack: (config, { isServer }) => {
if (!isServer) {
// Configure Monaco Editor for minimal build
config.plugins.push(
new MonacoWebpackPlugin({
languages: ["javascript"],
filename: "static/[name].worker.js",
features: ["!gotoSymbol"], // Disable heavy features
})
);
}
// Add this module rule to handle SVG as React components for Production
config.module.rules.push({
test: /\.svg$/,
use: ["@svgr/webpack"],
});
// Optimize bundle size for serverless functions
if (isServer) {
config.externals = [...(config.externals || []), "fs", "path", "os"];
}
return config;
},
};

25443
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

66
package.json Normal file
View File

@@ -0,0 +1,66 @@
{
"name": "linumiq-docs",
"version": "0.0.1",
"private": true,
"scripts": {
"predev": "node scripts/check-pagefind.js",
"dev": "tinacms dev -c \"next dev --turbopack\"",
"build": "echo 'Starting TinaCMS build...' && tinacms build && echo 'TinaCMS build completed. Starting Next.js build...' && next build",
"postbuild": "npx pagefind --site .next --output-path .next/static/pagefind && next-sitemap",
"build-local-pagefind": "tinacms build && next build && npx pagefind --site .next --output-subdir ../public/pagefind",
"export": "tinacms build && EXPORT_MODE=static,UNOPTIMIZED_IMAGES=true next build",
"start": "tinacms build && next start",
"lint": "biome check src/ tina/",
"lint:fix": "biome check src/ tina/ --fix",
"cleanup": "node scripts/cleanup.js",
"test": "playwright test",
"test:ui": "playwright test --ui"
},
"dependencies": {
"@heroicons/react": "^2.2.0",
"@monaco-editor/react": "^4.7.0",
"@next/third-parties": "^16.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-tabs": "^1.1.12",
"copy-to-clipboard": "^3.3.3",
"date-fns": "^4.1.0",
"fast-glob": "^3.3.3",
"html-to-md": "^0.8.8",
"lodash": "^4.17.21",
"mermaid": "^11.6.0",
"monaco-editor": "^0.52.2",
"motion": "^12.15.0",
"next": "^15.4.10",
"next-themes": "^0.4.6",
"prism-react-renderer": "^2.4.1",
"prismjs": "^1.30.0",
"react": "^19.2.3",
"react-animate-height": "^3.2.3",
"react-dom": "^19.2.3",
"react-dropzone": "^14.3.8",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
"rehype-pretty-code": "^0.14.1",
"shiki": "^3.6.0",
"tinacms": "^3.4.1",
"title-case": "^4.3.2",
"typescript": "5.8.3"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@playwright/test": "^1.54.2",
"@shikijs/transformers": "^3.6.0",
"@svgr/webpack": "^8.1.0",
"@tailwindcss/postcss": "^4.1.8",
"@tinacms/cli": "^2.1.5",
"@types/node": "^24.2.1",
"autoprefixer": "^10.4.21",
"monaco-editor-webpack-plugin": "^7.1.0",
"next-sitemap": "^4.2.3",
"pagefind": "^1.3.0",
"postcss": "^8.5.4",
"postcss-preset-env": "^10.2.0",
"tailwindcss": "^4.1.8",
"worker-loader": "^3.0.8"
}
}

68
playwright.config.ts Normal file
View File

@@ -0,0 +1,68 @@
import { defineConfig, devices } from "@playwright/test";
/**
* @see https://playwright.dev/docs/test-configuration
*/
export default defineConfig({
testDir: "./tests/e2e",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Use parallel workers in CI for faster execution */
workers: process.env.CI ? 2 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [
["html"],
["json", { outputFile: "test-results/results.json" }],
["junit", { outputFile: "test-results/results.xml" }],
],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: process.env.BASE_URL || "http://localhost:3000",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
/* Take screenshot on failure */
screenshot: "only-on-failure",
/* Record video on failure */
video: "retain-on-failure",
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
// {
// name: "Mobile Safari",
// use: { ...devices["iPhone 12"] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
webServer: process.env.BASE_URL
? undefined
: {
command: "pnpm dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
},
});

15685
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
"@tailwindcss/postcss": {},
autoprefixer: {},
},
};

2
public/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
index.html
assets/

2
public/admin/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
index.html
assets/

BIN
public/docs-starter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

8
public/favicon.svg Normal file
View File

@@ -0,0 +1,8 @@
<svg width="896" height="896" viewBox="0 0 896 896" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="448" cy="448" r="448" fill="#d0f1f8" />
<g transform="translate(224, 113)">
<path d="M287.399 275.676C317.558 250.731 330.922 103.315 343.977 50.3249C357.033 -2.6648 411.027 0.0227954 411.027 0.0227954C411.027 0.0227954 397.009 24.4176 402.727 42.6222C408.444 60.8267 447.612 77.0994 447.612 77.0994L439.165 99.3704C439.165 99.3704 421.524 97.114 411.027 118.112C400.53 139.109 424.177 344.885 424.177 344.885C424.177 344.885 354.684 471.46 354.684 524C354.684 576.539 379.552 620.572 379.552 620.572H344.655C344.655 620.572 293.462 559.65 282.963 529.203C272.464 498.757 276.662 468.31 276.662 468.31C276.662 468.31 221.017 465.161 171.674 468.31C122.331 471.46 89.4239 513.876 83.4849 537.603C77.5459 561.329 75.0859 620.572 75.0859 620.572H47.4931C30.6987 568.744 17.3603 550.169 24.5963 524C44.6393 451.512 40.7023 410.399 36.0616 392.088C31.4209 373.776 0 357.794 0 357.794C15.3916 326.436 31.1027 311.368 98.6838 309.796C166.265 308.225 257.239 300.62 287.399 275.676Z" fill="#EC4815"/>
<path d="M106.714 520.964C106.714 520.964 113.844 586.948 151.724 620.573H184.186C151.724 583.798 148.184 487.941 148.184 487.941C131.673 493.317 108.814 512.565 106.714 520.964Z" fill="#EC4815"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/img/404-image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Some files were not shown because too many files have changed in this diff Show More