@markuxt/markuxt
v0.1.32
Published
A Markdown-first academic portal framework for laboratories, research groups, and knowledge communities, powered by Nuxt.
Readme
Markuxt
A Markdown-first academic portal framework for laboratories, research groups, and knowledge communities, powered by Nuxt 3.
Markuxt provides a complete theme layer — layouts, pages, components, content transformers, and i18n — so that consuming sites only need to provide content and configuration.
Features
- Markdown-driven content — Pages, members, publications, projects, positions, and news are all authored in Markdown with YAML frontmatter.
- Configurable navigation — Define which pages appear in the header, footer, and route guard via a single
navigationarray inappConfig. - Configurable member categories — Define the member groups (e.g. Faculty / Students / Alumni) in
appConfig; authors reference them from each member'scategory:frontmatter. - Internationalization — Built-in i18n support via
@nuxtjs/i18n. Consuming sites provide their own locale files. - Design system — CSS custom properties for colors, spacing, typography, and more. Easy to override.
- Content transformers — Binary assets (images, videos) placed next to Markdown files are automatically synced to
public/for static serving. - Mermaid diagrams — First-class support with proper font loading and overflow handling.
- LaTeX / KaTeX — Math rendering via
remark-math+rehype-katex. - 404 page — Custom error page with header and footer preserved.
- Dark / Light mode — Built-in toggle button in the header. Defaults to the OS preference; the user's choice is persisted to
localStorage.
Theme Templates
Markuxt defaults to the Seaside theme (ocean blue/teal palette). Three additional themes are available as separate template repositories — each extends markuxt and applies its own CSS color overrides:
| Theme | Palette | Demo | |-------------|-------------------------|--------------------------------------------------------------------------------------------------| | Seaside | Ocean blue / teal | markuxt.github.io/markuxt-template-seaside | | Forest | Nature green | markuxt.github.io/markuxt-template-forest | | Sunset | Warm amber / terracotta | markuxt.github.io/markuxt-template-sunset | | Slate | Cool gray / steel blue | markuxt.github.io/markuxt-template-slate |
To start a new site with a specific theme, clone the corresponding template repo and follow its README. The theme is applied entirely through CSS custom properties in styles/_theme.css — no build-time configuration needed.
Architecture
markuxt/
├── nuxt.config.ts # Layer configuration (modules, hooks, vite, etc.)
├── app.config.d.ts # TypeScript declarations for markuxt AppConfig
├── src/
│ ├── assets/main.css # Design system & global styles
│ ├── components/ # Vue components (AppHeader, AppFooter, Hero, etc.)
│ ├── composables/ # Shared composables
│ ├── content-transformers/ # Custom Nuxt Content transformers
│ ├── error.vue # Custom 404 error page
│ ├── layouts/default.vue # Default layout with header + footer
│ ├── middleware/ # Route middleware (navigation guard)
│ ├── pages/ # All page routes
│ │ ├── index.vue # Homepage
│ │ ├── members/ # Members listing & detail pages
│ │ ├── news/ # News listing & detail pages
│ │ ├── positions/ # Open positions
│ │ ├── projects/ # Projects listing & detail pages
│ │ └── publications/ # Publications listing & detail pages
│ ├── plugins/ # Nuxt plugins
│ └── server/plugins/ # Server-side Nitro plugins
└── package.jsonConfiguration Reference
Consuming sites configure Markuxt through appConfig.markuxt in their nuxt.config.ts:
export default defineNuxtConfig({
appConfig: {
markuxt: {
// Logo image path
logo: {
src: '/images/logo.png',
},
// Navigation items — controls header nav, footer quick links,
// and route guarding. Pages NOT listed here will return 404.
navigation: [
{ to: '/', labelKey: 'nav.home' },
{ to: '/members', labelKey: 'nav.members' },
{ to: '/publications', labelKey: 'nav.publications' },
{ to: '/projects', labelKey: 'nav.projects' },
{ to: '/positions', labelKey: 'nav.positions' },
{ to: '/news', labelKey: 'nav.news' },
],
// Contact information shown in footer
contact: {
email: '[email protected]',
externalUrl: 'https://www.example.edu',
externalLabelKey: 'footer.universityLink', // i18n key
},
// Homepage carousel
carousel: {
fallbackImage: '/images/logo.png',
images: [
{ src: '/images/photo1.jpg', alt: 'Lab', caption: 'Our Lab' },
],
},
// Research areas on homepage — `icon` references a globally
// registered Vue component name (see Icons section below)
researchAreas: [
{ icon: 'IconSearch', titleKey: 'research.aerospace', descKey: 'research.aerospaceDesc' },
],
// Member categories — `key` is referenced from each member's
// `category:` frontmatter; `labelKey` is an i18n key. Array order is
// the display / filter / sort order. No built-in defaults — declare
// your own.
members: {
categories: [
{ key: 'staff', labelKey: 'members.staff' },
],
},
},
},
})Navigation Guard
Pages defined in navigation are accessible. Any markuxt section page (/members, /publications, /projects, /positions, /news) that is not listed will return a 404. The home page (/) is always accessible.
Member Categories
The groups on the Members page are configurable via members.categories. Each entry's key matches the category: field in a member's Markdown frontmatter, and labelKey is resolved against the consuming site's i18n files. The array order is the display, filter, and sort order; a filter bar appears automatically when there are 2+ categories, plus an All option. There are no built-in defaults — every consuming site declares its own categories. With none configured, the Members page simply lists everyone (no filter bar).
Icons
Markuxt does not bundle any icon library. The consuming site is responsible for registering Vue components globally and referencing them by name in configuration.
Example using @icon-park/vue-next:
plugins/icons.ts (project root, outside src/ to avoid Content scanning):
import Search from '@icon-park/vue-next/es/icons/Search'
import Robot from '@icon-puxt/vue-next/es/icons/Robot'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.component('IconSearch', Search)
nuxtApp.vueApp.component('IconRobot', Robot)
})nuxt.config.ts:
export default defineNuxtConfig({
plugins: ['~~/plugins/icons.ts'],
appConfig: {
markuxt: {
researchAreas: [
{ icon: 'IconSearch', titleKey: 'research.search', descKey: 'research.searchDesc' },
],
},
},
})i18n
Markuxt expects consuming sites to provide translation files. Translation keys used by Markuxt's components (e.g. nav.home, footer.brand, members.staff) must be present in the site's locale JSON files.
Content Structure
Markuxt uses @nuxt/content v2. The consuming site configures the content source directory:
content: {
sources: {
content: {
driver: 'fs',
base: resolve(process.cwd(), 'src'),
},
},
},Expected content directories under the base:
| Path | Content Type |
|-----------------|---------------------------------|
| members/ | Member profiles (Markdown) |
| news/ | News articles (Markdown) |
| publications/ | Publication entries (Markdown) |
| projects/ | Project descriptions (Markdown) |
| positions/ | Open positions (Markdown) |
Binary assets (images, videos) placed alongside Markdown files are automatically synced to public/_markuxt/ during build.
