# Full content corpus ## Markdown code guide URL: https://papyrus.marcelofelix.com/posts/code-demo/ Summary: Markdown route showing Pure-style Astro/Shiki code blocks, callouts, tables, diagrams, and media. Updated: 2026-07-01T13:00:00.000Z Source: src/content/posts/docs/authoring/10-code-demo.md This route is a compact rendering reference for Markdown-heavy posts. It shows how callouts, code fences, tables, diagrams, media, and fallback artifact links look inside the same article layout used by regular Papyrus posts. Inline samples such as `inline code`, ~~strikethrough~~, and https://github.com/marcelofpfelix/papyrus keep the prose checks close to the code checks. ## Obsidian callout syntax > [!NOTE] > Notes render as theme-aware callouts while keeping the original Markdown > readable in source form. > [!TIP] > Tips use the same callout component shape with a different token mix. > [!IMPORTANT] > Important callouts use their own icon and color so they are distinct from tips. > [!WARNING] > Warning variants use warning tokens instead of hardcoded colors. > [!CAUTION] > Caution callouts stay readable in light and dark modes. > [!WARNING]- Collapsed warning > Collapsed callouts keep long warnings available without dominating the page. > [!TIP]+ Expanded tip > Expanded callouts can stay open when the content is immediately useful. ## Code title ```rust title="src/main.rs" fn main() { println!("papyrus"); } ``` ## Diff fence ```css title="diff.css" .papyrus-card { background: var(--papyrus-panel); /* [!code --] */ background: transparent; /* [!code ++] */ color: var(--papyrus-accent); /* [!code ++] */ } ``` ## Highlighted lines ```c title="highlight.c" #include int main(void) { puts("papyrus"); // [!code highlight] return 0; } ``` ## Console fences ```console site$ pnpm install site# pnpm build site> pnpm preview ``` ## Collapsible code ```rust title="src/server.rs" use std::net::TcpListener; fn main() -> std::io::Result<()> { let listener = TcpListener::bind("127.0.0.1:4321")?; for stream in listener.incoming() { let stream = stream?; handle(stream); } Ok(()) } fn handle(_stream: T) { println!("papyrus"); } ``` ## Kamailio fences ```kamailio title="kamailio.cfg" #!KAMAILIO listen=udp:127.0.0.2:5060 loadmodule "sl.so" modparam("sl", "bind_tm", 0) request_route { if (is_method("INVITE")) { xlog("L_INFO", "call from $si to $ru\n"); sl_send_reply("100", "Trying"); } } ``` ## Task list - [x] Keep feature walkthroughs explicit - [ ] Document the source files beside rendered artifacts - [ ] Choose a renderer before embedding external diagram formats ## Table | Feature | Expected behavior | | --- | --- | | Code title | Render a compact title bar | | Diff | Style added and removed lines | | Table | Stay readable without heavy borders | ## Mermaid fence ```mermaid flowchart LR Markdown --> Code Markdown --> Alerts Markdown --> Diagrams ``` ## Image zoom ## Artifact links - [Mermaid source](/demo/theme-flow.mmd "Hydrated Mermaid source") - [PlantUML source](/demo/call-flow.puml "PlantUML source") - [Excalidraw source](/demo/sketch.excalidraw "Excalidraw source") ## Fallback rendering Some GitHub-style or diagram-adjacent formats need a site-owned plugin or renderer. Papyrus keeps the source visible so authors can choose the right integration for their site. | Feature | Expected fallback | | --- | --- | | PlantUML inline rendering | Keep as a file link | | Excalidraw inline rendering | Keep as a file link | | Wiki links like `[[topic]]` | Keep as plain text | | Footnotes like `[^1]` | Render when a consuming site adds a plugin | ## Generated content structure URL: https://papyrus.marcelofelix.com/posts/content-structure/ Summary: How Papyrus turns nested Markdown folders and folder metadata into a readable public content index. Updated: 2026-07-01T13:10:00.000Z Source: src/content/posts/docs/authoring/11-content-structure.md Use folder metadata when a site needs nested notes, guides, or knowledge-base pages without turning source paths into fragile public URLs. ## What gets generated Papyrus can read a folder tree, combine explicit frontmatter with folder-level labels, and render a public index that still feels hand-authored. The package fixture in `public/demo/content-tree` generates `public/demo/content-structure.md`. ## How to use it Keep canonical slugs in frontmatter when a page has a permanent URL. Use folders for organization, inherited tags, and section labels. This lets authors move files while the published route, RSS item, search result, and AI metadata stay stable. ```console pnpm papyrus-content-outline public/demo/content-tree public/demo/content-structure.md ``` - Use folder names for broad groups such as `guides`, `notes`, or `reference`. - Use an index file or folder metadata to name a section for readers. - Set `date` or `pubDatetime` to a future UTC timestamp to schedule a post. By default the post route, sitemap, search index, RSS, and AI exports are built, but home, posts, tag, and timeline lists hide the entry until the timestamp is reached. - When `date` has no time, Papyrus treats it as midnight UTC for scheduling and sorting. - Mark draft, private, or internal posts with `hidden: true` when they need a direct route but should stay out of public lists, feeds, sitemaps, search, and AI exports. - Add explicit `robots` frontmatter only when the direct page itself should be blocked from indexing. ## Public boundary User-facing docs now live as regular posts under `src/content/posts/docs`. Development-only notes stay under `.agents/` and are not linked from navigation unless they are intentionally rewritten as user documentation. ## Source artifact The generated Markdown outline remains available as a public fixture at [`/demo/content-structure.md`](/demo/content-structure.md). ## Markdown authoring guide URL: https://papyrus.marcelofelix.com/posts/markdown-feature-sample/ Summary: A practical guide showing how Papyrus renders Markdown, callouts, code, media, diagrams, and source actions. Updated: 2026-06-30T10:00:00.000Z Source: src/content/posts/docs/authoring/12-markdown-feature-sample.md Papyrus uses regular Astro Markdown. A post stays readable as plain text and builds into an article page with source, share, tag, table-of-contents, and metadata actions when the site enables them. This page is a quick rendering reference. Every section below is ordinary Markdown that a real post can use. ## Headings # H1 inside content ## H2 inside content ### H3 inside content #### H4 inside content ##### H5 inside content ###### H6 inside content ## Paragraph features Normal text remains readable. **Bold text**, *italic text*, ***bold italic text***, ~~strikethrough text~~, `inline code`, and [normal links](https://astro.build/) all sit cleanly in a paragraph. Autolinks stay visible: https://github.com/marcelofpfelix/papyrus Escaped characters remain literal: \*not italic\* and \`not code\`. ## Images ![Papyrus layout preview](/images/papyrus-layout.svg) ![Papyrus dark mode preview](/images/papyrus-dark.svg) ## Theme-aware SVGs Inline SVGs can follow the active theme when they use `currentColor` or Papyrus CSS variables: ```html ``` Use `currentColor` for single-color icons. Use `var(--papyrus-bg)`, `var(--papyrus-fg)`, `var(--papyrus-muted)`, `var(--papyrus-panel)`, and `var(--papyrus-accent)` for multi-color SVGs that belong to the theme. An SVG loaded through `` is its own document. It should include its own `var(--papyrus-*, fallback)` colors if it needs theme-like colors; Papyrus does not recolor arbitrary uploaded SVG files. ## Lists Unordered list: - keep the site repo focused on content and configuration - import reusable components from `astro-papyrus` - avoid copying a whole upstream theme into each site - keep overrides small enough to review Ordered list: 1. Write the post in `src/content/posts`. 2. Let Astro build the static route. 3. Use Papyrus layouts for repeated post UI. Nested list: - Theme - layout - post list - prose styles - Site - content - config - minimal pages Task list: - [x] base layout - [x] post layout - [x] search entry point - [ ] graph view ## Table | Feature | Type | Current state | | --- | --- | --- | | RSS | feed | working | | Tags | metadata | route and search filter | | Archive | index | visible when hidden posts exist | | Graph view | data | generated from the AI graph export | Right and center alignment: | Left | Center | Right | | :--- | :---: | ---: | | alpha | beta | 10 | | longer value | middle | 200 | ## Blockquotes > A good theme makes normal markdown readable before it adds more features. Nested quote: > First level > > > Second level ## GitHub alerts > [!NOTE] > Notes are calm and readable. > [!TIP] > Tips stand out without becoming noisy. > [!IMPORTANT] > Important text is easy to scan. > [!WARNING] > Warnings stay visible in both light and dark mode. > [!CAUTION] > Caution blocks keep the page rhythm intact. ## Code Inline code like `pnpm build` keeps paragraph line-height calm. TypeScript with a title and highlighted lines: ```ts title="src/pages/posts/index.astro" {1,4} import { PapyrusBaseLayout, PapyrusPostList } from "astro-papyrus/components"; import { publishedPosts } from "astro-papyrus/utils"; const posts = publishedPosts(await getCollection("posts")); ``` Rust: ```rust title="src/main.rs" {1,9-12} #[derive(Debug, Clone)] struct Repo { owner: String, name: String, } impl Repo { fn slug(&self) -> String { format!("{}/{}", self.owner, self.name) } } fn main() { let repo = Repo { owner: "marcelofpfelix".into(), name: "papyrus".into(), }; println!("{}", repo.slug()); } ``` Diff with add/remove line styling: ```diff title="papyrus.css" - .papyrus-icon-button:hover { - background: var(--papyrus-panel); - border-color: var(--papyrus-accent); - } + .papyrus-icon-button:hover { + background: transparent; + color: var(--papyrus-accent); + } ``` Shell: ```sh pnpm install pnpm build pnpm papyrus-llms src/content/posts public "$SITE_URL" ``` ## Mermaid ```mermaid flowchart LR Site[Astro site] --> Theme[Papyrus] Theme --> Pure[astro-pure] Theme --> Style[Papyrus CSS] Site --> Content[Markdown posts] ``` Diagram source links: [Mermaid source](/demo/theme-flow.mmd "Open the Mermaid source file") [PlantUML source](/demo/call-flow.puml "Open the PlantUML source file") [Excalidraw sketch](/demo/sketch.excalidraw "Open the editable Excalidraw file") ## Link preview Astro The web framework used by this blog and theme wrapper. astro.build ## Footnotes Footnotes are useful for small asides without breaking the main flow.[^1] [^1]: This is a GitHub-style footnote. ## Definition list Papyrus : reusable layouts, components, and CSS Site : content, config, and route composition ## Details Raw HTML details block This checks whether HTML inside markdown keeps spacing and typography. ## Horizontal rule --- The content after the rule stays connected to the rest of the post. ## What this page shows Public posts can double as useful documentation. Readers see how authoring features work, and maintainers get one page that covers headings, prose, images, lists, callouts, code, diagrams, artifact links, link previews, footnotes, definition lists, and raw HTML details. ## Deploy Papyrus URL: https://papyrus.marcelofelix.com/posts/deploy/ Summary: Static hosting, build settings, metadata URLs, and base-path notes for a Papyrus site. Updated: 2026-07-01T15:00:00.000Z Source: src/content/posts/docs/deploy/30-deploy.md Papyrus builds static Astro output. Publish the generated `dist` directory to any static host. ## Local preview Use the local development server while editing content, routes, and theme configuration. For a production-shaped preview, build first and serve the generated output. ```console pnpm install pnpm dev pnpm build pnpm preview ``` For a static preview of the built package demo, use the package Makefile: ```sh make serve ``` The default preview URL is printed by the command. ## Docker preview The Dockerfile is for repeatable local static preview of the package demo. It does not deploy anything. ```sh make docker-build make docker-run ``` Open `http://localhost:4327/`, then stop the container: ```sh make docker-stop ``` ## Static hosting The deployment artifact is the Astro `dist` directory. Cloudflare Pages, Netlify, Vercel static output, GitHub Pages, and any static file server can host it as long as the configured site URL matches the final domain. - Set the production site URL before generating sitemap, robots, RSS, social metadata, and AI indexes. - Keep secret lookup and host-specific deploy wrappers outside the reusable theme package. - Check `/robots.txt`, `/sitemap-index.xml`, `/rss.xml`, `/search/`, `/posts/`, and `/collections/` after publishing. ## Build settings Most static hosts need only the package manager, build command, output directory, and production URL. Keep the URL in one environment variable so generated metadata agrees across sitemap, robots, RSS, social previews, search, and AI files. ```text package manager: pnpm build command: SITE_URL="https://example.test" pnpm build output directory: dist ``` If the site uses pnpm, keep `minimumReleaseAge` in `pnpm-workspace.yaml` so production builds do not pick up packages published only minutes ago. ## Cloudflare Pages The package repository pins Wrangler as a dev dependency, so no global Wrangler install is required for the demo deploy target. One-time setup: - create or confirm the Cloudflare Pages project - create a token with Cloudflare Pages write access - store the token outside this repo Repeatable deploy command: ```sh CLOUDFLARE_API_TOKEN="$TOKEN" make deploy-demo ``` The target is repeatable because it updates the same Pages project: ```sh make deploy-demo PAGES_PROJECT=papyrus ``` Do not put personal token lookup helpers in this package. Wrap the command locally when a machine needs its own secret manager. ## Subdirectory deploys When the site is published under a base path, use Papyrus base-path helpers or Astro route helpers instead of hardcoded root-relative strings. Configure Astro's `base` option in the site, then keep internal links on helpers such as `withBase()` or `getRelativeLocaleUrl()`. ## After deploy Open the published URL and confirm the generated routes load. At minimum, check that `/robots.txt`, `/sitemap-index.xml`, `/rss.xml`, `/search/`, `/posts/`, and `/collections/docs/` match the configured site URL. ## Feature map URL: https://papyrus.marcelofelix.com/posts/feature-map/ Summary: Route-by-route guide to public Papyrus features. Updated: 2026-07-01T14:00:00.000Z Source: src/content/posts/docs/references/20-feature-map.md This is a map of the demo site. Each link points to a route or doc that shows the feature in context. ## Feature map - [Install and configure Papyrus](/collections/docs/install-configure-papyrus/) covers the template setup flow. - [Layout, header, footer, and theme controls](/) are visible on the home page. - [Site config](/collections/docs/site-config/) shows identity, navigation, projects, feature flags, and post-list defaults. - [Markdown authoring guide](/collections/docs/markdown-feature-sample/) shows Pure-style Astro/Shiki code blocks, alerts, task lists, tables, Mermaid SVG, artifacts, and zoom hooks. - [Posts index and tags](/posts/) show pinned ordering, list-only archive view, tag links, compact metadata, timeline link, and small covers. - [Search and tag filters](/search/) work with tag filters, standalone tag pages, and generated RSS feeds for individual tags. - [Collections](/collections/docs/collections/) explains folder-backed post collections detected from TOML files. - [CV/profile templates](/collections/docs/profile/) use normalized CV data across resume, timeline, projects, A4 links, and print routes. - [Projects and GitHub cards](/projects/) show the Papyrus theme and Papyrus template repositories. - [Papyrus package shape](/collections/docs/papyrus-package-shape/) explains `src` vs `public`, package exports, post list views, and RSS feed choices. - [Deploy and local preview](/collections/docs/deploy/) covers local dev, production preview, static hosting, base-path deploys, and deploy guidance. - [AI and mobile readiness](/collections/docs/ai-mobile/) covers generated metadata, search, responsive layout, and mobile authoring guidance. ## How to use it Repository-only implementation notes stay outside visitor-facing docs. ## AI and mobile readiness URL: https://papyrus.marcelofelix.com/posts/ai-mobile/ Summary: AI metadata, generated indexes, and mobile-ready defaults in a Papyrus site. Updated: 2026-07-01T14:10:00.000Z Source: src/content/posts/docs/references/21-ai-mobile.md Papyrus builds static metadata and search files alongside the site. The same build also keeps the main layouts usable on small screens. ## Generated files - `llms.txt` and `llms-full.txt` for agent-readable site summaries. - Static RSS feeds per tag. - Static JSON indexes for posts, tags, projects, notes, and CV summary data. - Static graph data export connecting posts, tags, projects, notes, and CV sections. - Stable IDs in generated JSON indexes, graph nodes, graph edges, and search records. - Static search data for local tools and Pagefind. - Per-post JSON-LD metadata for schema.org types. - Post layout support for canonical URL, source Markdown URL, author metadata, copy Markdown, and copy citation. Generated files include [`llms.txt`](/llms.txt), [`llms-full.txt`](/llms-full.txt), [`posts.json`](/ai/posts.json), [`tags.json`](/ai/tags.json), [`cv.json`](/ai/cv.json), and [`graph.json`](/ai/graph.json). ## Site-specific metadata - Add extra source links when a post depends on diagrams, notebooks, or external datasets. - Add JSON-LD types for people, projects, software source code, or breadcrumbs on site-specific pages. - Generate backlinks or related posts from Markdown links when the content model needs a knowledge graph. - Store explicit license metadata for images, code snippets, and diagrams when licensing matters. - Publish diagram source beside rendered SVG output for Mermaid, PlantUML, or Excalidraw workflows. ## Mobile defaults - Safe-area padding for phones with notches or rounded display edges. - 44px coarse-pointer hit targets for core icon, copy, post action, tag, TOC, and back-to-top controls. - Responsive post lists with stable cover dimensions and compact metadata. - Collapsible table of contents and back-to-top controls designed for small screens. - Image, SVG, and Mermaid zoom behavior that works with touch input. - Reduced-motion handling for cursor, logo, header, dropdown, and page-transition effects. - Profile and CV routes that share one data source across web, timeline, and print layouts. - Static Pagefind search and generated tag pages, so mobile search does not require a server. ## Mobile authoring tips Keep post descriptions short enough to scan in list views, prefer real cover images or generated covers with stable aspect ratios, use headings in order, and preview layout-heavy changes on desktop and phone-sized screens before publishing. ## Papyrus package shape URL: https://papyrus.marcelofelix.com/posts/papyrus-package-shape/ Summary: Package-boundary guide showing how Papyrus keeps repeated blog UI in reusable theme exports. Updated: 2026-06-30T11:00:00.000Z Source: src/content/posts/docs/references/24-papyrus-package-shape.md Papyrus keeps site repos focused on content and config. The site owns posts, profile data, assets, and deployment. The package owns shared UI: layouts, headers, footers, post lists, tags, archive helpers, profile/CV components, metadata helpers, and CSS. ![Papyrus centered layout](/images/papyrus-layout.svg) A content site usually needs the same core pieces on several routes: - a clean post list - tags and tag pages - timeline or archive page - RSS feed - reading time - previous and next post links - light and dark mode - a search entry point in the header - generated metadata for RSS, sitemap, robots, search, and AI indexes The package boundary stays explicit. Papyrus builds on Pure, adds publishing features, exports components and helpers, and lets the site override content, config, and routes. ```ts import { PapyrusBaseLayout, PapyrusPostList } from "astro-papyrus/components"; import { publishedPosts } from "astro-papyrus/utils"; ``` The goal is small imports, little local code, and theme updates in one package. ## Site files In a site repo, `src/` is source and `public/` is deployed static input. - Put posts, collections, custom pages, profile data, and content config in `src/`. - Put logos, favicons, covers, avatars, and generated static artifacts in `public/`. - Keep site copy in Markdown, TOML, or data files instead of hardcoding it in theme components. If a site needs a custom route, add that route in the site. If the route is generic enough for every Papyrus site, it belongs in the theme package. ## Post list views The public posts page uses the standard list view so the archive stays scannable: ```astro title="src/pages/posts/index.astro" ``` The same component can render denser or card-like post groups inside custom sections: ```astro title="post-list-view-reference.astro" ``` Keeping these examples inside a doc keeps `/posts/` focused on the main archive. ## RSS feed choices The normal feed at `/rss.xml` is the broad subscription. Tag feeds are separate URLs, so a reader can subscribe only to the topics they want instead of filtering after the fact: - [`/rss/tags/custom.xml`](/rss/tags/custom.xml) follows posts tagged with custom authoring features. - [`/rss/tags/changelog.xml`](/rss/tags/changelog.xml) follows release and package-change notes. For selective subscriptions, publish one feed per tag or section and link to the useful ones from the relevant post. ## Collections URL: https://papyrus.marcelofelix.com/posts/collections/ Summary: What Papyrus collections are and how folder-backed collection routes work. Updated: 2026-07-13T08:10:00.000Z Source: src/content/posts/docs/references/25-collections.md A collection is a folder of posts with a TOML file. Use one when readers should follow posts in a fixed order instead of normal blog date order. The same Markdown post can appear in the regular `/posts/` archive and in a collection route. The archive sorts by publishing metadata; the collection sorts by filename so authors can control the sequence. ## Folder shape Create a folder under `src/content/posts` and add a TOML file inside it: ```txt src/content/posts/ docs/ docs.toml start/ 00-papyrus-docs.md 03-site-config.md references/ 25-collections.md ``` Papyrus detects TOML files below `src/content/posts`. The collection slug comes from the folder name, so `src/content/posts/docs/docs.toml` becomes `/collections/docs/`. ## Collection config ```toml title="src/content/posts/docs/docs.toml" name = "Papyrus docs" description = "Package docs for installing, composing, and extending Papyrus." [settings] post_footer = "collection" post_footer_collapsible = true [[sections]] name = "Start" description = "Getting started and configuration." [[sections]] name = "References" description = "Reusable route and content references." ``` Sections are matched by folder slug. A section named `Start` reads posts from `start/`; a section named `References` reads posts from `references/`. ## Ordering Collection pages use filename order, not post date order. Prefix filenames when the order matters: ```txt start/00-papyrus-docs.md start/01-features.md start/02-install-configure-papyrus.md start/03-site-config.md ``` Keep explicit `slug` frontmatter when moving a post into a collection. That preserves the normal `/posts/my-slug/` route while also adding the collection route at `/collections/docs/my-slug/`. ## Post pages inside a collection Collection post pages receive collection-aware navigation: - the back link points to the collection page - previous and next links follow filename order - the table of contents can include collection sections - the footer can show the current collection, collapsed by default Use collection routes when sequence matters. Use normal post routes when date order is enough. ## Hiding collection posts `hidden: true` keeps a post reachable by direct URL but removes it from public collection lists, feeds, tag pages, sitemap, search, and AI indexes. Use it for fixtures and private-ish drafts that should not be part of public discovery. ## Profile and CV URL: https://papyrus.marcelofelix.com/posts/profile/ Summary: What the Papyrus profile data file controls and how profile, print, and export routes share it. Updated: 2026-07-13T08:20:00.000Z Source: src/content/posts/docs/references/26-profile.md The profile page reads `src/data/profile.toml`. The same data feeds the web profile page, timeline view, print routes, generated Markdown, and generated JSON. Keep personal data in this TOML file instead of hardcoding it in components. That makes the site easier to review, export, and print. ## User fields ```toml title="src/data/profile.toml" [user] name = "Site Author" avatar = "images/avatar.svg" bio = "Writer and software engineer" url = "site.test" print_color = "#37474F" email_user = "hello" email_domain = "site.test" links = ["linkedin", "github"] print_links = ["email", "linkedin", "github", "website"] location = "Lisbon, Portugal" born = "1991-04-18T00:00:00" roles = ["Software Engineer", "Technical writer"] sections = ["about", "experience", "education", "skills"] ``` Set site-wide avatar treatment in `papyrus.config.toml`: ```toml title="papyrus.config.toml" [profile.images] effect = "tritone" ``` The site default applies to profile-page images such as the avatar and company or education logos. Use `avatar_effect` in this file only when one profile should override that site default for the avatar. Supported values are `none`, `duotone`, `tritone`, `dither`, and `dithernoise`. Hide profile tabs per profile with `[user.profile_tabs]`: ```toml title="src/data/profile.toml" [user.profile_tabs] timeline = false projects = false ``` Available keys are `resume`, `timeline`, `projects`, and `skills`. Omitted keys default to visible. Use `email_user` and `email_domain` instead of a single literal email address when you want the page to assemble the visible contact with less obvious static scraping. It is not cryptographic protection; it only avoids the most basic email harvesters. ## Links Each named link can define a display name, base URL, and icon: ```toml title="src/data/profile.toml" [user.github] name = "site-owner" url = "https://github.com/" icon = "github" [user.linkedin] name = "site-owner" url = "https://www.linkedin.com/in/" icon = "linkedin" ``` The web profile can show a broader set of links through `links`. Print routes can use a smaller set through `print_links`. ## Sections Sections are declared in `[user.data.*]`. They can represent about text, experience, education, skills, interests, projects, or any site-specific CV group that follows the same structure. ```toml title="src/data/profile.toml" [user.data.experience] title = "Professional Experience" icon = "briefcase" page = 1 groups = ["company"] [user.data.experience.range] a = "2022-01-01T00:00:00" b = "2026-07-01T00:00:00" [user.data.experience.company] entity = "Company" url = "https://github.com/marcelofpfelix/papyrus" items = ["role"] [user.data.experience.company.role] title = "Senior Engineer" dates = "2022 - Present" location = "Remote" description = "Built and maintained content-heavy web systems." [user.data.experience.company.role.range] a = "2022-01-01T00:00:00" ``` Date ranges are used for calculated age and experience durations. Items without dates can still appear in the profile, but timeline-style views should only show dated material. ## Skills Skills can be written as tags and prose. Tag-style skills can link naturally to tag pages when the site uses matching post tags. Papyrus only links a skill tag when a public post already has that tag, so the profile does not create dead tag links. ```toml title="src/data/profile.toml" [user.data.skills.skills.skills] tags = ["astro", "markdown", "theme", "search"] description = """-- Astro content collections and static builds.
-- Markdown rendering, callouts, code blocks, and diagrams.""" ``` Keep skill icons optional. If a site uses technology logos, prefer a maintained set such as Devicon or skill-icons, and keep the printable CV mostly text-first. Skill chips should help readers find related writing or projects, not behave like subjective progress bars. ## Topic feeds Papyrus can generate one RSS feed per tag with `pnpm run rss-tags`. This lets readers subscribe only to the topics they care about, such as release notes, technical posts, or personal notes, without splitting the site into separate blogs. ## Export and print Optional encrypted-contact links can be configured without changing the profile component: ```toml title="src/data/profile.toml" pgp_key = "https://keys.openpgp.org/search?q=0123456789ABCDEF" pgp_fingerprint = "0123 4567 89AB CDEF" ``` Run the export command after editing profile data: ```sh pnpm run cv:export ``` The command writes `public/cv/resume.json` and `public/cv/profile.md`. Web routes use the TOML source. The generated files are useful for sharing, source actions, external review, and tools that understand the JSON Resume shape. The profile page links to print routes. The modern print route and classic ATS route use the same data, so the site does not maintain multiple resumes by hand. ## Papyrus docs URL: https://papyrus.marcelofelix.com/posts/papyrus-docs/ Summary: Start with papyrus-template, then edit config, Markdown, profile data, and assets. Updated: 2026-07-01T09:00:00.000Z Source: src/content/posts/docs/start/00-papyrus-docs.md Papyrus is a reusable Astro theme for personal sites, technical notes, project pages, and profile/CV pages. Start from `papyrus-template`, keep your content in that site, and let `astro-papyrus` provide the shared routes and components. ## Read first - [Install and configure Papyrus](/collections/docs/install-configure-papyrus/) explains the template workflow. - [Site config](/collections/docs/site-config/) shows what belongs in `papyrus.config.toml`. - [Markdown authoring guide](/collections/docs/markdown-feature-sample/) shows posts, code blocks, callouts, media, and source actions. - [Collections](/collections/docs/collections/) explains ordered docs or guide sections. - [Profile and CV](/collections/docs/profile/) explains the TOML-driven profile, print routes, and exports. ## For developers - [Papyrus package shape](/collections/docs/papyrus-package-shape/) explains what stays in the theme package and what stays in a site. - [Feature map](/collections/docs/feature-map/) maps public routes to reusable features. - [AI and mobile readiness](/collections/docs/ai-mobile/) covers search, generated metadata, mobile layout, and agent-readable files. - [Deploy Papyrus](/collections/docs/deploy/) covers static hosting and production builds. ## What to edit Most sites only need these files: - `papyrus.config.toml` for site identity, navigation, theme, feature flags, and homepage counts - `src/data/projects.toml` for project cards - `src/content/posts/` for posts, docs, and collections - `src/data/profile.toml` for profile and CV data - `public/` for images, logos, favicons, and static files The package repo has more files because it is the theme. A site made from `papyrus-template` should stay much smaller. Repo-only notes stay under `.agents/`; public docs live in this collection. ## Feature config URL: https://papyrus.marcelofelix.com/posts/features/ Summary: Grouped feature toggles for Papyrus layouts. Updated: 2026-07-01T09:10:00.000Z Source: src/content/posts/docs/start/01-features.md Use one typed config object to disable optional layout and post features without copying theme components. ## Base layout ```astro --- import { PapyrusBaseLayout } from "astro-papyrus/components"; const features = { header: true, footer: true, scrollHeader: true, search: false, rss: false, themeControls: true, poweredBy: true, }; ---

No search icon and no RSS icon on this page.

``` ## Post layout ```astro --- import { PapyrusPostLayout } from "astro-papyrus/components"; const features = { share: false, toc: false, postTags: true, aiMetadata: true, sourceActions: false, postStats: false, postSideLinks: false, adjacentPosts: false, backToTop: false, }; ---

Rendered without share, TOC, source actions, stats, or adjacent links.

``` ## SEO and social metadata Papyrus layouts emit the metadata a small public site normally needs: titles, descriptions, canonical URLs, Open Graph, Twitter cards, RSS discovery, and post JSON-LD. Hidden posts stay out of public indexes. Use explicit `robots` frontmatter when a direct page should emit directives such as `noindex, follow`. The build also generates social preview images for the homepage and every post. When a post has `cover`, the generated card can use that image as part of the card while still keeping the final output at the correct social-card size. Posts without a visible cover still get a generated Open Graph and X card. `papyrus.config.toml` also supports generic verification meta tags, opt-in analytics, typed custom head entries, and optional `security.txt` output. Keep analytics disabled by default, then enable only the provider a site actually uses. ## Base path deploys Internal URLs in shared layouts and list components go through `withBase()` and related helpers such as `stripBase()`, `stripLocale()`, `getAssetPath()`, and `getRelativeLocaleUrl()`. This keeps navigation, post links, RSS links, favicon assets, covers, tags, collection links, adjacent posts, project cards, and 404 suggestions working when Astro is deployed under a subdirectory. ## Search, tags, sitemap, and robots Public posts can be found through Pagefind search, tag pages, and optional per-tag RSS feeds. Regenerate the sitemap during every build so new posts, tag pages, profile pages, project pages, and collection routes are included automatically. Papyrus also includes a dynamic `robots.txt` route that reads the site URL, keeps crawl rules near site config, and points crawlers to the sitemap index. When `[security_txt]` has a contact, Papyrus publishes `/.well-known/security.txt` and `/security.txt`. Without a contact, those routes stay absent. ## Plugin contract Community plugins stay small: they declare capabilities, optional feature defaults, and site-owned integration points. They do not mutate Papyrus internals. Routes stay native Astro pages in the consuming app unless a plugin explicitly documents a route capability. ## Install and configure Papyrus URL: https://papyrus.marcelofelix.com/posts/install-configure-papyrus/ Summary: Use papyrus-template as the recommended starting point, then configure the site with TOML, Markdown, and asset overrides. Updated: 2026-07-10T09:00:00.000Z Source: src/content/posts/docs/start/02-install-configure-papyrus.md Start with [`papyrus-template`](https://github.com/marcelofpfelix/papyrus-template). The template keeps the site repo small: config, profile data, posts, and assets. The package provides the routes, layouts, post UI, collections, RSS, robots, search hooks, profile pages, and theme CSS. ## Start from the template Create a repository from `papyrus-template`, then install dependencies: ```sh pnpm install pnpm dev ``` The template depends on the package: ```json title="package.json" { "dependencies": { "astro-papyrus": "^0.2.2" } } ``` Use the npm package for normal sites. It is the stable path for template users because it resolves like any other dependency: ```sh pnpm add astro-papyrus@^0.2.2 ``` Use a pinned GitHub dependency only when testing unreleased Papyrus work or waiting for a new npm release to pass the registry maturity window: ```sh pnpm add github:marcelofpfelix/papyrus#0.2.2 ``` A branch name is convenient for preview work, but a tag or commit SHA is safer for real sites because it makes rebuilds repeatable. It enables Papyrus in Astro: ```js title="astro.config.mjs" import { definePapyrusAstroConfig } from "astro-papyrus/astro"; export default definePapyrusAstroConfig(); ``` ## Optional Astro plugins Papyrus exports small Astro integrations for features that should not be active on every site. Add only the ones the site needs: ```js title="astro.config.mjs" import { definePapyrusAstroConfig } from "astro-papyrus/astro"; import { papyrusBasePath, papyrusLinkValidator, papyrusMdTxt, papyrusSiteGraph, } from "astro-papyrus/plugins"; export default definePapyrusAstroConfig({ plugins: [ papyrusBasePath(), papyrusMdTxt(), papyrusSiteGraph(), papyrusLinkValidator(), ], }); ``` - `papyrusBasePath()` rewrites root-relative links and images in Markdown when Astro is deployed below a base path. Components continue to use Papyrus's `withBase()` helper. - `papyrusMdTxt()` publishes each public post at `/posts/.md.txt` with minimal title and description frontmatter. Its AST cleaner follows `starlight-md-txt` and removes MDX-only wrappers while preserving their readable content. - `papyrusSiteGraph()` adds `/graph/` from the existing `public/ai/graph.json` index. The normal Papyrus build generates that file before Astro runs. Papyrus keeps its small SVG renderer because the current `starlight-site-graph` release does not run correctly with Astro 7. - `papyrusLinkValidator()` checks Markdown links with source positions and rendered internal links after the Astro build. It fails on missing routes, including sites deployed below a base path. These integrations adapt useful ideas from the Starlight plugin ecosystem to Papyrus's routes and data. They are not a compatibility layer for running arbitrary Starlight plugins unchanged. Papyrus uses Pagefind as its single search engine. It intentionally does not wrap `starlight-telescope`, because that would add Starlight and Fuse to a site that already has a generated Pagefind index. Exact upstream releases, commits, adaptation boundaries, and licenses are listed in `THIRD_PARTY_NOTICES.md` in the package. Custom routes can read the same TOML file through `loadPapyrusConfig`: ```ts import { loadPapyrusConfig } from "astro-papyrus/config"; ``` It also reuses the Papyrus content collection: ```ts title="src/content.config.ts" export { collections } from "astro-papyrus/content"; ``` Papyrus adds these routes: | Route | Source | | --- | --- | | `/` | Home page with latest posts and project cards | | `/posts/` | Public post list | | `/posts/[...slug]/` | Post detail page | | `/projects/` | Project cards from `src/data/projects.toml` | | `/profile/`, `/profile/print/`, `/profile/ast/` | Profile and CV pages from `src/data/profile.toml` | | `/tag/` and `/tag/[tag]/` | Tag index and tag detail pages | | `/404.html` | Helpful not-found page | | `/rss.xml` | Main RSS feed | | `/robots.txt` | Robots file with sitemap URL | Because the pages are injected by the package, the template does not need a `src/pages` tree unless your site adds custom routes. ## Know `src` vs `public` Papyrus follows Astro's normal file boundary: `src/` is source, and `public/` is copied to the deployed site as-is. Use `src/` for files Astro should read, transform, type-check, or route during the build: - `src/content/posts/*.md` for posts - `src/content/posts/**/folder.toml` for ordered collections - `src/content.config.ts` for the Papyrus content collection export - `src/data/profile.toml` for the profile and CV source - `src/pages/*.astro` only when you need a custom route Use `public/` for files that should keep the same URL and contents after build: - `public/logo.svg`, `public/favicon.svg`, and `public/site.webmanifest` - `public/images/*` for covers, avatars, and project images - generated artifacts such as `public/cv/resume.json`, `public/cv/profile.md`, `public/ai/*`, `public/rss/tags/*`, and `public/pagefind/*` Do not hand-edit generated files in `public/`. Edit the source in `src/`, `papyrus.config.toml`, or `src/data/profile.toml`, then regenerate artifacts. ## Edit the right file For normal site work, start with these files: | Goal | Edit | | --- | --- | | Site title, description, navigation, theme, feature flags, homepage counts, and post-card defaults | `papyrus.config.toml` | | Project cards | `src/data/projects.toml` | | Add or edit posts | `src/content/posts/*.md` | | Add ordered docs or guide sections | `src/content/posts//.toml` plus Markdown posts | | Change the profile, CV, links, skills, dates, and print color | `src/data/profile.toml` | | Change logos, favicons, covers, avatars, and project images | `public/` assets | The package repo has many files because it owns the reusable components, routes, scripts, styles, and demo fixtures. A site repo should stay closer to the template shape: config, content, profile data, and assets. ## Add posts Posts live in `src/content/posts`. A minimal post needs a title, description, date, and optional tags. ```md title="src/content/posts/publishing-with-papyrus.md" --- title: Publishing with Papyrus description: A short implementation note published from a Papyrus-powered site. date: 2026-07-10T09:00:00.000Z tags: [astro, papyrus] cover: /images/cover.svg --- Write the post body in Markdown. ``` Papyrus handles list pages, detail pages, adjacent links, tags, scheduled posts, reading time, cover images, RSS entries, generated route paths, and generated social preview cards. A post does not need `cover` frontmatter to get an Open Graph/X sharing image. When a post has a cover, Papyrus uses it inside the generated social card instead of pointing `og:image` directly at the original cover file. Use `ogSourceImage` when the best social-card visual is an image from the post body instead of the article cover: ```md --- title: Publishing with Papyrus description: A short implementation note published from a Papyrus-powered site. date: 2026-07-10T09:00:00.000Z cover: /images/article-cover.jpg ogSourceImage: /images/body-diagram.jpg --- ``` The preview image order is: 1. `ogImage` points to a finished custom social card. 2. `ogSourceImage` feeds a body image into the generated social card. 3. `cover` feeds the visible post cover into the generated social card. 4. no image creates a generated text-only social card. Use `ogImage` only when you already have a finished `1200x630` social image and want to bypass the generated card for that post. ## Edit the profile The profile page reads `src/data/profile.toml`. ```toml title="src/data/profile.toml" [user] name = "Site Author" title = "Software Engineer" bio = "Writer and software engineer" location = "Lisbon, Portugal" print_color = "#37474F" email_user = "hello" email_domain = "site.test" sections = ["about", "experience", "education", "skills"] ``` The same profile data can be exported to JSON and Markdown with: ```sh pnpm run cv:export ``` ## Next steps - Configure the site in [Site config](/collections/docs/site-config/). - Learn content features in [Markdown authoring guide](/collections/docs/markdown-feature-sample/). - Use ordered docs or guides with [Collections](/collections/docs/collections/). - Review the package boundary in [Papyrus package shape](/collections/docs/papyrus-package-shape/). ## Site config URL: https://papyrus.marcelofelix.com/posts/site-config/ Summary: What papyrus.config.toml controls and how a site should edit it. Updated: 2026-07-13T08:00:00.000Z Source: src/content/posts/docs/start/03-site-config.md `papyrus.config.toml` is the site control file. Edit this before copying theme code into `src/pages` or local components. Use it for site identity, navigation, social links, theme defaults, feature flags, homepage behavior, and post-list behavior. Use frontmatter for per-post metadata. Use `src/data/projects.toml` for project cards and `src/data/profile.toml` for profile and CV data. Papyrus generates a homepage social card from site title and description and a post-specific social card for every Markdown post. `cover` is used as the visual source inside the generated card when it is present. Use `ogSourceImage` when a different post image should feed the generated card, or `ogImage` when a post needs to point at a finished custom social image. ## What it controls | Section | Purpose | | --- | --- | | `[site]` | Site title, description, URL, language, text direction, and timezone | | `[brand]` | Header brand title, mark, and whether the text title is visible | | `[theme]` | Default color profile and font profile | | `[home]` | Homepage counts and layout-facing defaults | | `[profile.images]` | Default visual treatment for profile images | | `[markdown]` | Prose styling presets for links, headings, lists, quotes, tables, and inline code | | `[pages.]` | Optional text overrides for inherited page descriptions | | `[verification]` and `[[verification_meta]]` | Search engine and service verification meta tags | | `[analytics]` | Optional production-only analytics script | | `[head]` | Constrained site-owned meta, link, and external script entries | | `[security_txt]` | Optional vulnerability disclosure contact for `security.txt` | | `[features]` | Optional UI, metadata, comments, search, graph, media, and profile features | | `[post_card]` | Post-list tags, read time, fresh indicators, updated-date behavior, and default limit | | `[[nav]]` | Header navigation links | | `[[footer]]` | Footer text links | | `[[social]]` | Footer social icon links | Papyrus reads this file with `loadPapyrusConfig()`. If it is missing, package defaults are used so a minimal template can still build. ## Minimal config ```toml title="papyrus.config.toml" [site] title = "My site" home_title = "hello, world" description = "Notes, projects, and profile." url = "https://site.test" lang = "en" dir = "ltr" timezone = "Europe/Lisbon" [brand] title = "My site" mark = "twinkle" show_title = true [theme] profile = "everforest" font_profile = "readable" [home] project_limit = 2 [profile.images] effect = "none" [[nav]] href = "/posts/" label = "Posts" [[nav]] href = "/profile/" label = "Profile" ``` Keep the URL set to the deployed origin. The same value is used for canonical metadata, RSS, sitemap, robots, social previews, and generated AI indexes. Use `home_title` only when the homepage heading should be different from the site title used by metadata, RSS, and shared layout chrome. ## Source links Set the public GitHub source once so profile source links and post source actions can point at the right repository: ```toml title="papyrus.config.toml" [source] repo = "site-owner/site-repo" ``` Papyrus uses that value for GitHub blob links and raw Markdown copy actions. The repo can be written as `owner/repo`, a GitHub URL, or an SSH GitHub remote. The branch is detected at build time on Cloudflare Pages, GitHub Actions, Vercel, and Netlify. Set `branch = "main"` only when you want a fixed branch. ## Page descriptions Inherited routes keep intro descriptions hidden by default. Add text only for the pages where the site should show it: ```toml title="papyrus.config.toml" [pages.posts] description = "Latest notes and release updates." [pages.projects] description = false ``` TOML does not support `null`. Use `description = false` when a route should not render a description or description meta tag. Omit the page entry to keep the Papyrus default. The inherited About page accepts longer paragraph content: ```toml title="papyrus.config.toml" [pages.about] description = "Short About page intro." content = """ Hey, I am Marcelo. I build and operate software around telecom, Linux, automation, and infrastructure. This site is where I keep technical notes, project logs, and occasional side interests. """ ``` Blank lines in `content` create separate paragraphs. The About page keeps this body focused and adds a profile link at the end. ## Verification, analytics, and head entries Keep verification and analytics disabled until the site has real provider values. Papyrus keeps the Google shorthand, but the generic verification table also covers Bing and other services: ```toml title="papyrus.config.toml" [seo] google_verification = "google-search-console-token" [verification] bing = "bing-webmaster-token" [[verification_meta]] name = "p:domain_verify" content = "pinterest-token" ``` Analytics is opt-in and does not load during local development unless `include_in_dev = true` is set. Supported providers are `ga4`, `plausible`, `umami`, `goatcounter`, and `custom`: ```toml title="papyrus.config.toml" [analytics] enabled = true provider = "plausible" domain = "site.test" ``` Comments are also opt-in. Papyrus renders Giscus on post pages when `[comments].enabled` is true and the Giscus repo and category IDs are configured. The feature flag defaults on; disable it with `[features].comments = false` only if you want to turn comments off globally. ```toml title="papyrus.config.toml" [comments] enabled = true repo = "site-owner/site-repo" repo_id = "R_..." category_id = "DIC_..." ``` Use a GitHub Discussions category with the `Announcements` format when possible. That lets Giscus create post discussions while preventing normal repository visitors from manually opening unrelated discussions in the comments category. Papyrus defaults to the Giscus provider, `Announcements` category, `preferred_color_scheme`, and `papyrus` comments themes. `papyrus` means Giscus loads a same-origin CSS file generated from the active Papyrus theme profile, such as `/giscus/gruvbox/dark.css` or `/giscus/catppuccin/light.css`. Override `category`, `theme`, `light_theme`, or `dark_theme` only when your site needs built-in Giscus themes or a custom CSS URL. Keep custom Giscus CSS on a URL you control. Giscus loads that stylesheet inside its iframe, so avoid example URLs or third-party CSS you do not trust. `mapping = "pathname"` is the recommended default. If you use `mapping = "specific"`, set `term`; if you use `mapping = "number"`, set `number`. Papyrus also accepts `description` and `back_link` for the matching advanced Giscus fields. For stricter control, add `giscus.json` to the repository that owns the Discussions. Use it for allowed origins and the default comment order: ```json title="giscus.json" { "origins": ["https://site.test"], "originsRegex": ["http://localhost:[0-9]+"], "defaultCommentOrder": "newest" } ``` Use `[head]` for small, typed additions that belong to the site. It accepts meta tags, link tags, and external scripts. It does not accept raw HTML strings. ```toml title="papyrus.config.toml" [[head.meta]] name = "fediverse:creator" content = "@site@example.social" [[head.link]] rel = "me" href = "https://example.social/@site" [[head.script]] src = "https://example.test/script.js" defer = true ``` Footer links use `[[footer]]` for normal text links and `[[social]]` for icon links. Add provider widgets as normal links when possible; only use `[head]` when a provider really needs a document-level tag or script. ## Security.txt Configure `security.txt` only when the site has a public vulnerability disclosure contact. With no contact, Papyrus returns a 404 for `/.well-known/security.txt` and `/security.txt` instead of publishing a placeholder file. ```toml title="papyrus.config.toml" [security_txt] contact = ["mailto:security@example.com"] preferred_languages = "en, pt" policy = "https://site.test/security" ``` ## Feature flags Feature flags are booleans. Set only the flags you want to override: ```toml title="papyrus.config.toml" [features] search = true comments = false postStats = false graph = false ``` Disable features that need external setup. For example, turn off comments and remote post stats until the site has configured those services. ## Profile images Use `src/data/profile.toml` for profile content and asset paths. Use `papyrus.config.toml` for the default image treatment: ```toml title="papyrus.config.toml" [profile.images] effect = "tritone" ``` The default is `none`. The configured effect applies to profile-page images such as the avatar and company or education logos. `duotone` uses the current background and foreground colors. `tritone` also uses the accent color. `dither` and `dithernoise` generate masks at build time and color them with theme-aware ink. A profile data file can still override the default for its own avatar with `avatar_effect`. Profile-local tab visibility belongs in `src/data/profile.toml` under `[user.profile_tabs]`, with keys such as `timeline = false` or `projects = false`. ## Markdown styling Markdown styling uses named presets instead of custom per-element colors. The presets map to the active theme tokens, so they keep working when the reader switches color mode or theme profile. ```toml title="papyrus.config.toml" [markdown] link_style = "accent-hover-underline" heading_style = "plain" marker_style = "accent" blockquote_style = "accent-bar" table_style = "horizontal" table_header_style = "muted" inline_code_style = "panel" ``` Supported values: | Option | Values | | --- | --- | | `link_style` | `accent`, `underline`, `accent-underline`, `accent-hover-underline` | | `heading_style` | `plain`, `accent`, `muted-accent` | | `marker_style` | `plain`, `muted`, `accent` | | `blockquote_style` | `muted-bar`, `accent-bar`, `panel` | | `table_style` | `none`, `horizontal`, `grid` | | `table_header_style` | `plain`, `muted`, `panel` | | `inline_code_style` | `plain`, `panel`, `accent-soft` | ## Post-list defaults Post-card options apply to normal post lists such as home and `/posts/`: ```toml title="papyrus.config.toml" [post_card] tags = false read_time = false fresh_indicators = true fresh_indicator_text = true updated_date_only = true limit = 20 ``` When `tags = false`, the first tag can still be shown as plain context in the date line. When `updated_date_only = true`, list cards show the update date for updated posts while the post page can still show both created and updated dates. ## Projects and links Use repeated TOML tables for navigation, footer links, and social links in `papyrus.config.toml`: ```toml title="papyrus.config.toml" [[footer]] href = "/collections/docs/" label = "Docs" [[social]] href = "https://github.com/site-owner" label = "GitHub" icon = "github" ``` Keep project cards in `src/data/projects.toml`: ```toml title="src/data/projects.toml" [[project]] title = "My project" description = "A short public project summary." href = "/projects/my-project/" image = "/images/project.svg" repo = "https://github.com/site-owner/project" pinned = true status = "active" [[project.links]] href = "https://github.com/site-owner/project" label = "repo" text = "site-owner/project" ``` Use `public/` for images referenced by config. Use `src/` for content and data that Astro should parse or transform. ## Related files - `astro.config.mjs` wires the Papyrus integration and loads this config. - `src/content.config.ts` exports the Papyrus content collection. - `src/data/profile.toml` owns profile, CV, timeline, print, and source export data. - `src/content/posts/**` owns posts, docs, and collection content. ## Choco Frito URL: https://papyrus.marcelofelix.com/posts/choco-frito/ Summary: Setubal-style fried cuttlefish strips served with lemon, fries, and salad. Updated: 2024-10-20T09:00:00.000Z Source: src/content/posts/food/fish/00-choco-frito.md Choco Frito is crisp fried cuttlefish, strongly associated with Setubal. ## Ingredients - Cuttlefish strips - Garlic, bay leaf, and lemon - Flour or cornmeal - Oil for frying - Fries and salad ## Method Season the cuttlefish, coat lightly, fry until crisp, and serve with lemon and simple sides. ## Bacalhau a Bras URL: https://papyrus.marcelofelix.com/posts/bacalhau-a-bras/ Summary: Shredded salt cod folded with onions, matchstick potatoes, eggs, olives, and parsley. Updated: 2024-01-20T09:00:00.000Z Source: src/content/posts/food/fish/01-bacalhau-a-bras.md Bacalhau a Bras is one of the most familiar Portuguese cod dishes. ## Ingredients - Desalted shredded cod - Onion and garlic - Matchstick potatoes - Eggs - Black olives and parsley ## Method Soften the onion, add cod and potatoes, then fold in beaten eggs off direct heat so the mixture stays creamy. ## Ameijoas a Bulhao Pato URL: https://papyrus.marcelofelix.com/posts/ameijoas-a-bulhao-pato/ Summary: Clams cooked quickly with garlic, olive oil, white wine, coriander, and lemon. Updated: 2024-09-20T09:00:00.000Z Source: src/content/posts/food/fish/02-ameijoas-a-bulhao-pato.md Ameijoas a Bulhao Pato is a fast clam dish with a bright garlic-coriander sauce. ## Ingredients - Fresh clams - Garlic - Olive oil - White wine - Coriander and lemon ## Method Cook garlic in olive oil, add clams and wine, cover until opened, then finish with coriander and lemon. ## Bacalhau com Natas URL: https://papyrus.marcelofelix.com/posts/bacalhau-com-natas/ Summary: Salt cod baked with potatoes, onion, cream, and a golden breadcrumb top. Updated: 2024-02-20T09:00:00.000Z Source: src/content/posts/food/fish/03-bacalhau-com-natas.md Bacalhau com Natas is a creamy baked cod dish with potatoes and onion. ## Ingredients - Desalted cod flakes - Potatoes - Onion and garlic - Cream or bechamel - Breadcrumbs ## Method Layer cod, potatoes, and onion in a baking dish, cover with cream sauce, and bake until bubbling and golden. ## Cataplana de Peixe URL: https://papyrus.marcelofelix.com/posts/cataplana-de-peixe/ Summary: Fish and shellfish steamed with tomato, pepper, onion, herbs, and white wine. Updated: 2024-08-20T09:00:00.000Z Source: src/content/posts/food/fish/04-cataplana-de-peixe.md Cataplana de Peixe uses a sealed pan to steam fish with vegetables and wine. ## Ingredients - Firm white fish - Clams or prawns - Tomato, pepper, onion, and garlic - White wine - Coriander and olive oil ## Method Layer everything in the cataplana, close it, and cook until the fish is just done and the broth is aromatic. ## Bacalhau a Gomes de Sa URL: https://papyrus.marcelofelix.com/posts/bacalhau-a-gomes-de-sa/ Summary: Oven-baked cod with potatoes, onions, eggs, olives, parsley, and olive oil. Updated: 2024-03-20T09:00:00.000Z Source: src/content/posts/food/fish/05-bacalhau-a-gomes-de-sa.md Bacalhau a Gomes de Sa is a Porto cod classic with potatoes and plenty of olive oil. ## Ingredients - Desalted cod - Potatoes - Onion and garlic - Hard-boiled eggs - Olives, parsley, and olive oil ## Method Bake cod, sliced potatoes, and onions together, then finish with eggs, olives, parsley, and more olive oil. ## Polvo a Lagareiro URL: https://papyrus.marcelofelix.com/posts/polvo-a-lagareiro/ Summary: Tender octopus roasted with punched potatoes, garlic, olive oil, and greens. Updated: 2024-07-20T09:00:00.000Z Source: src/content/posts/food/fish/06-polvo-a-lagareiro.md Polvo a Lagareiro is tender octopus with roasted potatoes and generous olive oil. ## Ingredients - Octopus - Small potatoes - Garlic - Olive oil - Greens for serving ## Method Simmer the octopus until tender, roast it with crushed potatoes and garlic, then finish with plenty of hot olive oil. ## Sardinhas Assadas URL: https://papyrus.marcelofelix.com/posts/sardinhas-assadas/ Summary: Grilled sardines served with roasted peppers, potatoes, bread, and olive oil. Updated: 2024-04-20T09:00:00.000Z Source: src/content/posts/food/fish/07-sardinhas-assadas.md Sardinhas Assadas are simple grilled sardines, especially popular in summer. ## Ingredients - Fresh sardines - Coarse salt - Roasted peppers - Boiled potatoes - Bread and olive oil ## Method Salt the sardines, grill over high heat, and serve immediately with potatoes, peppers, bread, and olive oil. ## Arroz de Marisco URL: https://papyrus.marcelofelix.com/posts/arroz-de-marisco/ Summary: Brothy seafood rice with prawns, clams, mussels, tomato, coriander, and stock. Updated: 2024-06-20T09:00:00.000Z Source: src/content/posts/food/fish/08-arroz-de-marisco.md Arroz de Marisco is saucy seafood rice, served loose rather than dry. ## Ingredients - Short-grain rice - Prawns, clams, and mussels - Tomato, onion, garlic, and pepper - Seafood stock - Coriander ## Method Cook the rice in seafood stock and tomato base, then add shellfish near the end so it stays tender. ## Caldeirada de Peixe URL: https://papyrus.marcelofelix.com/posts/caldeirada-de-peixe/ Summary: Layered fish stew with potatoes, peppers, tomatoes, onions, herbs, and olive oil. Updated: 2024-05-20T09:00:00.000Z Source: src/content/posts/food/fish/09-caldeirada-de-peixe.md Caldeirada de Peixe is a layered fisherman's stew built without much stirring. ## Ingredients - Mixed firm fish - Potatoes - Tomato, pepper, onion, and garlic - White wine - Parsley, coriander, and olive oil ## Method Layer vegetables and fish in a pot, season well, cover, and simmer gently until the potatoes and fish are cooked. ## Cozido a Portuguesa URL: https://papyrus.marcelofelix.com/posts/cozido-a-portuguesa/ Summary: A slow Portuguese meat and vegetable stew with cabbage, potatoes, sausages, and beef. Updated: 2024-01-10T09:00:00.000Z Source: src/content/posts/food/meat/10-cozido-a-portuguesa.md Cozido a Portuguesa is a generous one-pot meal built from meats, smoked sausages, cabbage, potatoes, carrots, and turnips. ## Ingredients - Beef shank or brisket - Pork ribs or belly - Chourico and farinheira - Cabbage, potatoes, carrots, and turnips - Salt, pepper, and bay leaf ## Method Simmer the meats first, then add the vegetables in stages so everything finishes tender. Serve the broth, meats, and vegetables together on a wide platter. ## Leitao a Bairrada URL: https://papyrus.marcelofelix.com/posts/leitao-a-bairrada/ Summary: Crisp roast suckling pig seasoned with garlic, pepper, lard, and salt. Updated: 2024-10-10T09:00:00.000Z Source: src/content/posts/food/meat/11-leitao-a-bairrada.md Leitao a Bairrada is famous for crisp skin, peppery seasoning, and juicy meat. ## Ingredients - Suckling pig - Garlic, coarse salt, pepper, and lard - Bay leaf - Orange slices for serving - Simple salad or chips ## Method Rub the pig with seasoning, roast until the skin is crisp, and serve in slices with a bright, simple garnish. ## Carne de Porco a Alentejana URL: https://papyrus.marcelofelix.com/posts/carne-de-porco-a-alentejana/ Summary: Marinated pork cubes cooked with clams, fried potatoes, coriander, and pickles. Updated: 2024-02-10T09:00:00.000Z Source: src/content/posts/food/meat/12-carne-de-porco-a-alentejana.md Carne de Porco a Alentejana pairs paprika-marinated pork with clams and crisp potatoes. ## Ingredients - Pork shoulder, cut into cubes - Garlic, paprika, bay leaf, and white wine - Clams - Potatoes for frying - Coriander and pickled vegetables ## Method Marinate the pork, brown it, then steam the clams in the pan juices. Fold in the fried potatoes and finish with coriander and pickles. ## Prego no Prato URL: https://papyrus.marcelofelix.com/posts/prego-no-prato/ Summary: Garlic steak served on a plate with fries, rice, egg, salad, and mustard. Updated: 2024-09-10T09:00:00.000Z Source: src/content/posts/food/meat/13-prego-no-prato.md Prego no Prato is the plated version of the Portuguese garlic steak sandwich. ## Ingredients - Thin beef steak - Garlic, butter, and bay leaf - Fried egg - Rice, fries, and salad - Mustard ## Method Sear the steak in garlic butter, fry the egg, and serve with the classic sides and a spoonful of sauce from the pan. ## Francesinha URL: https://papyrus.marcelofelix.com/posts/francesinha/ Summary: Porto's layered sandwich with steak, sausage, ham, melted cheese, and beer-tomato sauce. Updated: 2024-03-10T09:00:00.000Z Source: src/content/posts/food/meat/14-francesinha.md Francesinha is a rich Porto sandwich covered with melted cheese and a spicy beer-tomato sauce. ## Ingredients - Thick bread slices - Steak, fresh sausage, and ham - Sliced cheese - Tomato, beer, stock, and piri-piri for the sauce - Fries for serving ## Method Grill the meats, stack the sandwich, cover it with cheese, and bake until melted. Pour hot sauce over the sandwich just before serving. ## Feijoada a Transmontana URL: https://papyrus.marcelofelix.com/posts/feijoada-a-transmontana/ Summary: Northern bean stew with pork, smoked sausages, cabbage, and rice. Updated: 2024-08-10T09:00:00.000Z Source: src/content/posts/food/meat/15-feijoada-a-transmontana.md Feijoada a Transmontana is a rich bean stew from the north, usually served with rice. ## Ingredients - Red or white beans - Pork ribs, ear, or belly - Chourico and morcela - Cabbage - Onion, garlic, paprika, and bay leaf ## Method Cook the beans and meats until soft, add cabbage near the end, and serve with plain rice. ## Bitoque URL: https://papyrus.marcelofelix.com/posts/bitoque/ Summary: A simple steak plate with fried egg, rice, fries, and a quick garlic pan sauce. Updated: 2024-04-10T09:00:00.000Z Source: src/content/posts/food/meat/16-bitoque.md Bitoque is a weekday classic: thin steak, fried egg, rice, fries, and a savory pan sauce. ## Ingredients - Thin beef steaks - Garlic, bay leaf, and butter - Eggs - Rice and fries - Mustard or white wine for the sauce ## Method Sear the steaks quickly, loosen the pan with wine or mustard, and serve with the fried egg on top. ## Tripas a Moda do Porto URL: https://papyrus.marcelofelix.com/posts/tripas-a-moda-do-porto/ Summary: Porto-style tripe stew with beans, smoked meats, carrot, and cumin. Updated: 2024-07-10T09:00:00.000Z Source: src/content/posts/food/meat/17-tripas-a-moda-do-porto.md Tripas a Moda do Porto is a hearty stew of tripe, beans, sausage, and aromatic spices. ## Ingredients - Cleaned tripe - White beans - Chourico and smoked meats - Onion, carrot, garlic, and bay leaf - Cumin and pepper ## Method Cook the tripe until tender, then stew it with beans and smoked meats until the sauce is thick and savory. ## Arroz de Pato URL: https://papyrus.marcelofelix.com/posts/arroz-de-pato/ Summary: Duck rice baked with broth, chourico, and a crisp golden top. Updated: 2024-05-10T09:00:00.000Z Source: src/content/posts/food/meat/18-arroz-de-pato.md Arroz de Pato turns tender duck and its broth into a baked rice dish topped with chourico. ## Ingredients - Duck legs - Onion, garlic, bay leaf, and carrot - Rice - Chourico slices - Parsley ## Method Poach the duck, shred the meat, cook rice in the broth, then bake with chourico until the top is lightly crisp. ## Cabrito Assado URL: https://papyrus.marcelofelix.com/posts/cabrito-assado/ Summary: Roast kid goat with garlic, white wine, bay leaf, potatoes, and paprika. Updated: 2024-06-10T09:00:00.000Z Source: src/content/posts/food/meat/19-cabrito-assado.md Cabrito Assado is a celebratory roast, often served with potatoes and greens. ## Ingredients - Kid goat pieces - Garlic, bay leaf, paprika, and white wine - Olive oil - Potatoes - Parsley and lemon ## Method Marinate overnight, roast slowly with potatoes, and baste until the meat is tender and the potatoes are browned. ## Theme-aware image effects URL: https://papyrus.marcelofelix.com/posts/image-effects/ Summary: Configure duotone, tritone, and dithered image treatments for covers and profile photos. Updated: 2026-08-13 Source: src/content/posts/image-effects.md Papyrus image effects are configured per image. Use them when a normal photo feels too detached from the active theme. `duotone` and `tritone` are CSS effects. The browser keeps the original image and recolors it with the current theme tokens, so light and dark mode switches happen immediately. Use `duotone` for background and foreground colors only. `dither` and `dithernoise` are different. Papyrus generates dark and light alpha masks at build time, then the browser paints the active mask with a theme-derived ink color. `dither` uses an Atkinson-style dot pattern. `dithernoise` uses the same pattern and steps through generated noise frames. original duotone tritone dither dithernoise Videos can opt into the runtime effect with the same theme ink. The video stays as normal media, and Papyrus upgrades it only when WebGL, motion, and visibility allow it. Open the video demo For a post cover, set the effect in frontmatter: ```yaml cover: /images/photo.jpg cover_effect: duotone ``` Use `dither` or `dithernoise` when you want a build-time mask: ```yaml cover: /images/photo.jpg cover_effect: dithernoise ``` For profile images, set the site default: ```toml [profile.images] effect = "tritone" ``` Or override one profile directly: ```toml [user] avatar = "images/profile.jpg" avatar_effect = "dither" ``` The supported values are `none`, `duotone`, `tritone`, `dither`, and `dithernoise`. For content videos, use `class="papyrus-video-effect-dither"` on a normal `` element with a poster. In Markdown, use `data-papyrus-video-dither-src` on a placeholder element and keep a simple link inside it. Papyrus keeps the video or link as the fallback and uses a lazy canvas enhancement for the dithered rendering.