Quarto Ontology Website Skill
Usage: Load this file into Claude Code or paste into an LLM session. It contains everything needed to build a professional personal website with Quarto, organized by a typed ontology system where every page declares what kind of content it is and metadata encodes relationships between pages.
This skill produces a static Quarto website where content pages have typed front matter (node-type: project, node-type: role, etc.), relationships are encoded as YAML metadata, and Quarto listings auto-generate index pages per type. No JavaScript, no database – just convention-based structure.
1) The Pattern
Every content page declares a node-type in its YAML front matter. Relationships between pages are encoded as metadata fields. Quarto’s listing feature auto-generates index pages from page metadata.
Person (index, about)
└── has Projects (projects/*.qmd)
└── uses Skills (skills: [r, python])
└── produced-during Role (role: acme-health)
└── held Roles (experience/*.qmd)
└── requires Skills (skills: [sql, r])
└── authored Publications (publications.qmd)
└── demonstrates Skills (skills: [statistics])
└── wrote Writings (writings/*.qmd)
└── categorized-as Topics (categories: [teaching])
The “graph” is the set of relationships in front matter. Quarto reads these fields and generates the site. No runtime query engine required.
2) Node Types and Their Front Matter
Person (landing page, about page)
---
title: "Your Name"
node-type: person
toc: false
---Project
---
title: "Project Title"
description: "One-sentence summary for listing cards."
node-type: project
status: active # active | complete | archived
role: acme-health # edge: produced-during → role page
skills: [r, tidymodels, shap] # edge: uses-skill
categories: [healthcare-ai] # Quarto cross-reference
date: 2024-01-01 # for listing sort order
github: "https://github.com/user/repo" # optional
---Role (professional experience)
---
title: "Job Title"
description: "Organization -- brief summary for listing cards."
node-type: role
organization: "Company Name"
start-date: 2021-02-01
skills: [r, sql, azure] # edge: requires-skill
categories: [healthcare-ai]
---Writing (essays, teaching materials, blog posts)
---
title: "Essay Title"
description: "Brief summary for listing cards."
node-type: writing
topic: bible-study # or: tutorial, essay, reflection
audience: small-group # or: developers, general
date: 2025-01-01
categories: [teaching, bible-study]
---Publication (peer-reviewed papers)
Publications can be individual pages or entries on a single page. For a single-page approach, use node-type: publication-index on the listing page.
Skill
Skills are typically listed on a single page (skills.qmd) rather than individual pages. Use node-type: skill-index.
Meta (self-describing pages)
---
title: "How This Site Works"
node-type: meta
---3) Edge Types (Relationships via Metadata)
| Edge | Encoding | Example |
|---|---|---|
uses-skill |
skills: [r, tidymodels] in project front matter |
Project uses these tools |
produced-during |
role: acme-health in project front matter |
Project done during this role |
demonstrates |
skills: [statistics] in publication metadata |
Paper demonstrates this expertise |
requires |
skills: [r, sql] in role front matter |
Role requires these skills |
categorized-as |
categories: [healthcare-ai] |
Standard Quarto categories |
4) Directory Structure
site-root/
├── _quarto.yml # Site config with nav and render rules
├── _ontology-schema.yml # Machine-readable schema (not rendered)
├── styles.css # Custom styling
├── index.qmd # Landing page (node-type: person)
├── about.qmd # Personal story (node-type: person)
├── publications.qmd # All publications on one page
├── skills.qmd # Skill inventory on one page
├── projects/
│ ├── index.qmd # Auto-listing of project pages
│ ├── _metadata.yml # Default front matter for projects
│ ├── project-one.qmd
│ └── project-two.qmd
├── experience/
│ ├── index.qmd # Auto-listing of roles
│ ├── _metadata.yml # Default front matter for roles
│ ├── role-one.qmd
│ └── role-two.qmd
├── writings/
│ ├── index.qmd # Auto-listing of writings
│ ├── _metadata.yml # Default front matter for writings
│ └── essay-one.qmd
├── ontology/
│ └── index.qmd # "How This Site Works" page
├── _archive/ # Old files (gitignored, not rendered)
└── _publish.yml # Deployment config (Netlify, GitHub Pages, etc.)
5) Key Configuration Files
_quarto.yml
project:
type: website
render:
- "*.qmd"
- "projects/"
- "experience/"
- "writings/"
- "ontology/"
- "!_archive/"
# Exclude any non-website directories with !dirname/
website:
title: "{SITE_TITLE}"
navbar:
left:
- href: index.qmd
text: Home
- href: about.qmd
text: About
- href: projects/index.qmd
text: Projects
- href: experience/index.qmd
text: Experience
- href: publications.qmd
text: Publications
- href: skills.qmd
text: Skills
- href: writings/index.qmd
text: Writings
- href: ontology/index.qmd
text: Ontology
right:
- icon: linkedin
href: https://www.linkedin.com/in/{LINKEDIN_HANDLE}/
- icon: github
href: https://github.com/{GITHUB_HANDLE}
format:
html:
theme: cosmo
css: styles.css
toc: trueKey points: - render: explicitly lists what to render and excludes non-site dirs with ! - Theme cosmo is clean and professional. Other good options: flatly, lux, minty - toc: true globally enables table of contents on all pages
Listing Index Pages
Each section with multiple pages gets a listing index:
---
title: "Projects"
listing:
contents: "*.qmd"
type: default
sort: "date desc"
fields: [title, description, categories]
filter-ui: false
sort-ui: false
---
Brief introduction text for the section.The listing auto-discovers all .qmd files in the same directory (excluding index.qmd itself) and generates cards from their front matter. No manual maintenance needed – add a new .qmd file with the right front matter and it appears automatically.
_metadata.yml (Section Defaults)
Each section directory gets a _metadata.yml that sets defaults:
# projects/_metadata.yml
node-type: projectThis means individual project pages don’t need to repeat node-type: project (though they can for clarity).
_ontology-schema.yml
Machine-readable schema documenting the type system. Not rendered by Quarto – serves as documentation for the convention.
node_types:
project:
required: [title, status, skills, summary]
optional: [role, github, dates, categories]
statuses: [active, complete, archived]
role:
required: [title, organization, start-date]
optional: [end-date, skills, highlights]
publication:
required: [title, year, url]
optional: [journal, domain, skills]
skill:
required: [name, category]
optional: [proficiency, evidence]
categories: [languages, frameworks, domains, tools]
proficiency_levels: [foundational, working, advanced, expert]
writing:
required: [title, date, topic]
optional: [audience, language]
edge_types:
uses-skill: { from: project, to: skill }
produced-during: { from: project, to: role }
demonstrates: { from: publication, to: skill }
requires: { from: role, to: skill }
categorized-as: { from: any, to: category }6) Page Templates
Landing Page (index.qmd)
---
title: "{YOUR_NAME}"
node-type: person
toc: false
---
::: {.hero-section}
# {YOUR_NAME}
::: {.lead}
{TITLE} | {DOMAIN_1} | {DOMAIN_2}
:::
{2-3 sentence professional summary. What you do, for whom, and what
makes your approach distinctive.}
:::
::: {.highlight-cards}
::: {.highlight-card}
### [{HIGHLIGHT_1_TITLE}]({LINK})
{2-sentence description of this area of work.}
:::
::: {.highlight-card}
### [{HIGHLIGHT_2_TITLE}]({LINK})
{2-sentence description.}
:::
::: {.highlight-card}
### [{HIGHLIGHT_3_TITLE}]({LINK})
{2-sentence description.}
:::
::: {.highlight-card}
### [{HIGHLIGHT_4_TITLE}]({LINK})
{2-sentence description.}
:::
:::
## Explore
- [About Me](about.qmd) -- {brief}
- [Projects](projects/index.qmd) -- {brief}
- [Experience](experience/index.qmd) -- {brief}
- [Publications](publications.qmd) -- {brief}
- [Skills](skills.qmd) -- {brief}
- [Writings](writings/index.qmd) -- {brief}
- [How This Site Works](ontology/index.qmd) -- {brief}About Page (about.qmd)
---
title: "About Me"
node-type: person
---
# About Me
{Personal introduction -- 1 paragraph in the person's own voice.}
## My Values and Approach
- **{Value 1}:** {Explanation}
- **{Value 2}:** {Explanation}
- **{Value 3}:** {Explanation}
## How I Work
{2-3 paragraphs about professional approach, current focus,
philosophy of work.}
---
## Let's Connect
{Call to action with LinkedIn and GitHub links.}Project Page (projects/{project-name}.qmd)
---
title: "{Project Title}"
description: "{One sentence for listing card.}"
node-type: project
status: active
role: {role-slug}
skills: [{skill1}, {skill2}, {skill3}]
categories: [{category1}, {category2}]
date: {YYYY-MM-DD}
github: "{REPO_URL}"
---
# {Project Title}
## Summary
{2-3 paragraphs: what it is, why it matters, what makes it interesting.}
## Key Methods
- **{Method 1}:** {Description}
- **{Method 2}:** {Description}
## Status
{Current state of the project and what's next.}Role Page (experience/{role-slug}.qmd)
---
title: "{Job Title}"
description: "{Organization} -- {brief summary.}"
node-type: role
organization: "{Organization}"
start-date: {YYYY-MM-DD}
skills: [{skill1}, {skill2}]
categories: [{category}]
---
# {Job Title}
**{Organization}** | {Date Range}
::: {.timeline-entry}
### What I Do
{1-2 paragraphs about the role.}
### Key Contributions
- **{Contribution 1}:** {Description}
- **{Contribution 2}:** {Description}
### Tools & Methods
{Comma-separated list of tools and technologies.}
:::Publications Page (publications.qmd)
---
title: "Publications"
node-type: publication-index
---
# Publications
{One-sentence intro.}
---
::: {.pub-entry}
### {Paper Title} {.pub-title}
::: {.pub-meta}
**Year:** {YYYY} | **Journal:** *{Journal Name}* | **Domain:** {Domain}
**Skills:** [{skill1}]{.badge}, [{skill2}]{.badge}
:::
{1-2 sentence summary of the paper.}
[PubMed]({URL}){.btn .btn-sm .btn-outline-primary}
:::Repeat the :::{.pub-entry} block for each publication.
Skills Page (skills.qmd)
---
title: "Skills"
node-type: skill-index
---
# Skills
{Brief intro.}
---
## {Category Name}
::: {.skill-category}
::: {.skill-list}
- [{Skill}]{.expert} -- {context}
- [{Skill}]{.advanced}
- [{Skill}]{.working}
:::
:::
---
*Proficiency levels:*
[expert]{.expert} = daily use, deep knowledge |
[advanced]{.advanced} = regular use, strong proficiency |
[working]{.working} = functional, growingOntology Page (ontology/index.qmd)
This is the “How This Site Works” page. It should explain:
- What node types exist and what they represent (with links to example pages)
- How relationships are encoded as YAML metadata (with a concrete example)
- Why this approach is useful (consistency, discoverability, the idea itself)
- What it is not (not a graph database, not JavaScript – convention-based)
- Where the schema lives (link to
_ontology-schema.yml)
7) Styles (styles.css)
/* Hero section on landing page */
.hero-section {
padding: 2rem 0 3rem 0;
margin-bottom: 2rem;
border-bottom: 2px solid #e9ecef;
}
.hero-section h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
.hero-section .lead {
font-size: 1.25rem;
color: #6c757d;
margin-bottom: 1.5rem;
}
/* Highlight cards on landing page */
.highlight-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin: 2rem 0;
}
.highlight-card {
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 1.5rem;
transition: box-shadow 0.2s;
}
.highlight-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.highlight-card h3 {
font-size: 1.1rem;
margin-bottom: 0.5rem;
color: #2c3e50;
}
.highlight-card p {
font-size: 0.95rem;
color: #555;
margin-bottom: 0;
}
/* Timeline styling for experience */
.timeline-entry {
border-left: 3px solid #2c3e50;
padding-left: 1.5rem;
margin-bottom: 2rem;
position: relative;
}
.timeline-entry::before {
content: '';
position: absolute;
left: -7px;
top: 0;
width: 12px;
height: 12px;
border-radius: 50%;
background: #2c3e50;
}
.timeline-entry h3 {
margin-top: 0;
}
/* Skill badges */
.skill-category {
margin-bottom: 2rem;
}
.skill-list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
list-style: none;
padding: 0;
}
.skill-list li {
background: #f1f3f5;
padding: 0.35rem 0.75rem;
border-radius: 4px;
font-size: 0.9rem;
}
.skill-list li.expert {
background: #2c3e50;
color: white;
}
.skill-list li.advanced {
background: #34495e;
color: white;
}
.skill-list li.working {
background: #d5e8d4;
}
/* Ontology diagram styling */
.ontology-diagram {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 1.5rem;
margin: 1.5rem 0;
}
/* Publication entries */
.pub-entry {
padding: 1rem 0;
border-bottom: 1px solid #eee;
}
.pub-entry:last-child {
border-bottom: none;
}
.pub-entry .pub-title {
font-weight: 600;
}
.pub-entry .pub-meta {
font-size: 0.9rem;
color: #6c757d;
}8) Build and Deploy
Render
quarto renderAll pages should render with no errors. Verify: - Listing pages populate (projects, experience, writings show entries) - Navigation works across all sections - No blank pages
Preview
quarto previewDeploy (Netlify)
# First time: creates _publish.yml with site ID
quarto publish netlify
# Subsequent deploys
quarto publish netlifyDeploy (GitHub Pages)
quarto publish gh-pages9) Adding New Content
Add a New Project
- Create
projects/new-project.qmdwith the project front matter template - Ensure
status,skills,categories, anddateare filled in - Run
quarto render– the listing page auto-discovers it
Add a New Role
- Create
experience/new-role.qmdwith the role front matter template - Ensure
organization,start-date, andskillsare filled in - Run
quarto render
Add a New Writing
- Create
writings/new-essay.qmdwith the writing front matter template - Ensure
topic,date, andcategoriesare filled in - Run
quarto render
Add a New Publication
Add a new :::{.pub-entry} block to publications.qmd.
Add a New Node Type
- Define the type in
_ontology-schema.yml - Create a directory with
index.qmd(listing) and_metadata.yml - Add the section to
_quarto.ymlnavbar and render list - Update
ontology/index.qmdto document the new type
10) Adapting the Schema
For a Software Developer Portfolio
node_types:
project:
required: [title, status, skills, summary]
optional: [role, github, demo-url, dates]
statuses: [active, complete, archived, maintained]
role:
required: [title, organization, start-date]
optional: [end-date, skills, team-size]
open-source:
required: [title, github, skills]
optional: [stars, contributors, language]
talk:
required: [title, date, venue]
optional: [slides-url, video-url, topic]For a Researcher / Academic
node_types:
project:
required: [title, status, skills, summary]
optional: [funding, collaborators, dates]
statuses: [active, complete, archived, submitted]
publication:
required: [title, year, journal]
optional: [doi, impact-factor, citations, preprint-url]
grant:
required: [title, funder, amount, dates]
optional: [role, status, collaborators]
course:
required: [title, institution, semester]
optional: [enrollment, level, materials-url]
student:
required: [name, degree, year]
optional: [thesis-title, current-position]For a Consultant / Freelancer
node_types:
engagement:
required: [title, client-type, skills, summary]
optional: [duration, industry, outcome]
service:
required: [title, description]
optional: [pricing-model, typical-duration]
testimonial:
required: [quote, client-type]
optional: [client-name, date, engagement]
case-study:
required: [title, challenge, solution, outcome]
optional: [skills, industry, duration]For an Artist / Creative
node_types:
work:
required: [title, medium, date]
optional: [series, dimensions, price, status]
statuses: [available, sold, exhibited, private]
exhibition:
required: [title, venue, date]
optional: [type, curator, works-shown]
collection:
required: [title, theme]
optional: [works, date-range]11) What Not to Do
- Don’t build a graph database. The value is in consistent structure, not in query infrastructure. YAML front matter is the graph.
- Don’t add JavaScript. Static Quarto is fast, accessible, and deployable anywhere. If you need interactivity, use Quarto’s built-in features (tabsets, collapsibles) not custom JS.
- Don’t over-type. Start with 4-6 node types. Add more only when you have content that doesn’t fit existing types. A type with one instance is premature.
- Don’t make the ontology page optional. The “How This Site Works” page is part of what makes this approach interesting. It shows you think about structure, not just content.
- Don’t skip the schema file.
_ontology-schema.ymlis cheap to create and documents the convention for future you (or a future LLM session).
12) Quarto Prerequisites
- Quarto >= 1.3 (for listing features)
- No R or Python needed for the site itself (it’s pure markdown + YAML)
- Deployment: Netlify, GitHub Pages, or any static host
Install: https://quarto.org/docs/get-started/
13) Worked Example: Complete Minimal Site
For a developer named “Alex Chen” with 2 projects and 1 role:
File count: 11 files total
_quarto.yml, styles.css, _ontology-schema.yml,
index.qmd, about.qmd, skills.qmd,
projects/index.qmd, projects/_metadata.yml, projects/my-tool.qmd,
experience/index.qmd, experience/current-job.qmd
This renders to 7 pages. Add writings/, publications.qmd, and ontology/ as content warrants. The structure scales from 7 pages to 70 without changing the architecture.