Contributing
Add or correct content, improve the site, and maintain its generated metadata
Contributor guide
Start here
What we welcome, how to send it, and how content is reviewed
Ways to contribute
cpp.social is a community-maintained project - your help is required to keep this site up to date and ensure we're listing the resources people actually care about.
If you notice missing resources, wrong descriptions and other mistakes or have general feedback on how to improve the usability to this site, please do reach out. While we prefer it where possible, you do not have to submit a pull request for your suggested changes - it is also possible to let us know through an issue.
For content submissions, please check for duplicates first and make sure the suggested content fulfills our content requirements.
Content PRs require two approvals
Every content change needs at least two approvals by current curators or maintainers before we can accept it. You can find a list of current maintainers and curators on the About page.
What we list
Content must be relevant to C++ developers and have a stable public page.
- Tutorials, guides, and other learning resources must be free to access in full, without a purchase or subscription. Books are of course exempt from this.
- Books, blogs, and YouTube channels must contain substantive human-created work. Purely AI-generated publications are not eligible.
- Tooling must have real users beyond personal experiments or toy projects. Include available evidence of use, such as public projects that depend on them.
Use direct links
Use canonical HTTPS URLs without tracking parameters. URL shorteners, referral or affiliate links, and vanity links are not accepted.
Submit a pull request
To locally build the site, you will need Python 3.13 or newer and Node.js 24 (the version used in CI). The rest of the tooling can be installed by invoking make install.
- Read the relevant guide below and edit the relevant file(s). Follow a nearby entry's structure and use only supported fields.
- Optional: If the entry needs fetched metadata, you may run the guide's updater command for that entry (do inspect the output to ensure there's no unrelated changes!).
- Run
make buildand check the affected page underbuild/. - Open a pull request against
cppsocial/site. Describe the change and explain why it was made.
For code changes, also run make check-format, make lint, and make test. If formatting fails, use the matching make format-* command shown in the failure message.
Generated files
Add content through the source files named in these guides, not by editing generated files under data/.
Related metadata changes are welcome
Content PRs may include related data/ changes, such as the initial ingest or a targeted refresh for a new blog or YouTube channel. Leave out metadata changes for unrelated entries.
However, this is purely optional.
Hand-maintained exceptions are covered in the Packages and Content maintenance guides.
Contributor guide
Directory entries
Resources, guides, ecosystem tools, standards links, and recurring conferences
Pick the destination
| Entry | File |
|---|---|
| General reference or online tool | content/resources/general.yaml |
| Guide, FAQ, or learning material | content/resources/guides.yaml |
| Compiler or toolchain | content/ecosystem/compilers.yaml |
| Build system or project generator | content/ecosystem/builds.yaml |
| Library or standard-library implementation | content/ecosystem/libraries.yaml |
| Package manager as a tool | content/ecosystem/packages.yaml |
| ABI resource | content/ecosystem/abi.yaml |
| General standardization resources | content/evolution/resources.yaml |
| Related technical standard | content/evolution/related-standards.yaml |
| Active recurring conference series | content/events/conferences.yaml |
| Conference series no longer running | content/events/past-conferences.yaml |
Events and registry packages have separate guides
For dated events, follow the Events guide. For packages listed in a registry, follow the Packages guide.
Add a card
Add a record under the existing cards list:
- title: Example Tool
path: https://example.org/
description: Short factual explanation of its use in C++
metadata:
License: MIT
links:
- {label: Documentation, path: https://example.org/docs/}
- {label: Source, path: https://github.com/example/tool}
Requirements
- Provide a concise
titleand the canonical HTTPSpath - Explain the entry's relevance to C++ in a short, factual
description - Check that the entry is public and not already listed; for active projects, look for recent releases or development
- For a project or tool, show that it has real users and is relevant to C++ developers
- Add
metadataand secondarylinksonly when they help someone evaluate or use the entry - Use labels already established in that section
- Follow the shared URL, description, and YAML rules in the Style guide
Icons and hidden entries
Omit icon to discover the site's favicon automatically. Set icon: null when no icon should be displayed. Use an explicit HTTPS image URL only when favicon discovery gives the wrong result.
No hidden placeholders
Use hidden: true only to hide an existing entry. New entries must be complete and visible.
Check the result
Run make build, open the affected page under build/, and check the card at narrow and wide widths. Confirm that every link works and that the description does not duplicate the title.
Contributor guide
Stylistic conventions
The conventions that apply across repository content
Descriptions
- Write one factual sentence and do not end it with a period
- Use the project's own description if it is clear and factual, otherwise summarize what it does
- Avoid first person, superlatives, calls to action, download counts, and other marketing claims
- Preserve the project's own spelling and capitalization
URLs and sources
- Use canonical HTTPS URLs without tracking parameters or fragments unless the fragment is essential
- Prefer first-party project, organizer, publisher, or repository pages
- Use a primary resource for event dates and corrections, not a search result or social repost
- Keep link labels short and consistent with nearby entries
- Discord invites must be permanent and cannot be vanity links
YAML
- Copy the indentation and field order of a nearby record
- Quote strings that YAML could misread, such as
"yes","0123", or text containing:or# - Use ISO
YYYY-MM-DDdates and IANA timezone names - Keep locally assigned IDs lowercase, unique, and stable
- Preserve external identifiers exactly, including case in YouTube channel IDs, Discord invite codes, and package IDs
- Do not reformat unrelated entries in a content pull request
The $schema line describes the whole document and should not be changed when adding a record. Unknown fields fail schema validation.
Markdown
Use semantic line breaks for prose in docs/ and other long-form Markdown. Use sentence-case headings, fenced code blocks with a language, and relative links for repository documentation.
Contributor guide
Communities
Discord, Slack, IRC, Reddit, forums, and meetup groups
Chat, forum, and Q&A communities
Add chat communities to the cards list in content/communities/im.yaml and forums or Q&A sites to the cards list in content/communities/forums.yaml. Every community_id must be unique and must not change after publication.
- community_id: example-community
metadata_source: {source: web, key: 'https://example.org/community/'}
title: Example C++ Community
platform: forum
path: https://example.org/community/
description: Public forum for C++ library design and implementation questions
Allowed platform values are discord, slack, irc, reddit, and forum. Allowed metadata sources are discord, web, reddit, and stackoverflow. For web, the key is the canonical page URL; for Reddit, it is the subreddit name without r/. For Stack Overflow, use the encoded tag c%2B%2B as the key.
Communities must be public, active, and substantially about C++. Use a direct community URL and a description of its audience or focus.
Discord
Non-expiring, non-vanity invites only
Use an active invite with no expiry. Server vanity or custom invites are not accepted.
Copy the code after discord.gg/ from the community's invite, or create an invite with expiry set to Never if you have permission. Use that code as both the URL suffix and metadata key:
- community_id: example-discord
metadata_source: {source: discord, key: Ab3dE9xYz2}
title: Example C++ Discord
platform: discord
path: https://discord.gg/Ab3dE9xYz2
description: Public chat for C++ developers working on embedded systems
The PR check uses Discord's API to check that the invite resolves, has no expiry, and is not the server's vanity code. Open it to confirm that it leads to the intended community.
Check the result
For a new community, fetch only its metadata, then build:
make communities ID=example-community
make build
For Meetup groups, run make events, then make build and inspect /events/. This refreshes all configured event feeds; leave unrelated changes out of the PR. See Generated files for when to include updater output.
Contributor guide
Blogs, books, and YouTube
Sources whose metadata is fetched after review
Blogs
Add the source to content/blogs/sources.yaml:
- id: example_blog
title: Example C++ Blog
author: Ada Example
website_url: https://example.org/
rss_url: https://example.org/feed.xml
The blog must publish substantial C++ content and provide a public RSS or Atom feed.
- Choose a unique lowercase
idbased on the publication name and follow the naming convention in the file. - Copy the title and canonical website URL from the blog. Include
authorif it names an individual author. - Find the feed through the site's RSS or Subscribe link, or the page source's RSS/Atom alternate link. Open it and check that it contains post URLs and publication dates.
- Add a description only if the title does not explain the source.
sitemap_url may be required to properly backfill the blog's content archive. You do not need to do this for a content PR, but you can if you want.
Backfilling is done by executing meta-updater blogs ingest --id example_blog. The normal make blogs command only fetches the RSS feed to discover new posts.
YouTube channels
Add a channel to the cards list in the file matching its owner:
| Channel | File | channel_type |
|---|---|---|
| Independent creator or educator | content/youtube/channels.yaml | creator |
| Conference archive | content/youtube/conferences.yaml | conference |
| Organization | content/youtube/organizations.yaml | organization |
| Podcast or recurring show | content/youtube/organizations.yaml | show |
- title: Example C++ Channel
channel_type: creator
channel_id: UCxxxxxxxxxxxxxxxxxxxxxx
The channel must publish substantial C++ material. Copy its title from the channel header and choose the file and channel_type from the table above.
Use the channel ID
Use the stable ID beginning with UC, not a handle, playlist, or video ID.
On desktop, open the channel, expand its description with more, then choose Share channel → Copy channel ID (YouTube Help Community instructions). For your own channel, you can also use Advanced settings. Open https://www.youtube.com/channel/<channel_id> to check that it is the intended channel.
Do not manually add video IDs, thumbnails, descriptions, or feed URLs - those are fetched automatically by the metadata updater.
Books
Add an ISBN record to the cards list in content/books/books.yaml (this example is already listed):
- { isbn: "9780321563842" }
Use the quoted 13-digit ISBN from the publisher page or copyright page for the exact edition and format you want to list. Hardbacks, paperbacks, ebooks, and later editions can have different ISBNs.
Book metadata is fetched automatically
Submit only the ISBN and, if needed, a cover override. Open Library supplies the title, author, publisher, and description.
Only add cover_url when Open Library has no usable cover or returns the wrong edition:
- isbn: "9780321563842"
cover_url: https://example.org/covers/9780321563842.jpg
The override must be a stable HTTPS image URL that the project is allowed to display.
Generated metadata
Run the command for the entry you added, then build:
make blogs ID=example_blog
make youtube ID=UCxxxxxxxxxxxxxxxxxxxxxx
make books ID=9780321563842
make build
The ID must already exist in the relevant curated file. To refresh multiple entries, repeat --id, for example meta-updater blogs all --id first_blog --id second_blog.
Check the affected page for the correct publication and metadata. Do not create or edit cache files by hand. See Generated files for when to include updater output in the PR.
Contributor guide
Events
Conferences, workshops, committee meetings, and individual meetup events
Meetup groups
Add a group to the cards list in content/events/meetups.yaml only when it exposes a public Meetup iCalendar feed. The updater imports individual occurrences from that feed.
- type: meetup_card
source_id: example-cpp
timezone: Europe/Paris
title: Example C++ Meetup
path: https://www.meetup.com/example-cpp/
description: Regular C++ meetup in Paris
metadata:
Location: Paris, France
links:
- {label: RSS feed, path: https://www.meetup.com/example-cpp/events/rss/}
- {label: Calendar, path: https://www.meetup.com/example-cpp/events/ical/}
Include the meetup type
Set type: meetup_card on the entry itself. The event importer does not use the group's card_type default.
Use the group name from meetup.com/<group-name>/ as source_id, in lowercase. Choose the group's timezone from IANA's list, for example Europe/Paris. Copy the group calendar feed using Meetup's export instructions. Include exactly one calendar link ending in /events/ical/ (the trailing slash is optional). Open the link to check that the feed is public. Do not copy imported occurrences into content/events/events.yaml.
Recurring series or dated occurrence
A recurring conference series is a directory card in content/events/conferences.yaml. Move a series to content/events/past-conferences.yaml when it is no longer running. Follow the Directory entries guide for that record.
Add a specific conference, workshop, committee meeting, or one-off meetup to the YAML list in content/events/events.yaml.
Do not duplicate imported events
Meetup events from configured calendar feeds are imported automatically. Do not add them to content/events/events.yaml.
Add a dated event
- ical_uid: exampleconf-2027@cpp.social
title: ExampleConf 2027
description: Two-day conference focused on C++ systems programming
start_date: 2027-04-12
end_date: 2027-04-13
location: Paris, France
venue: Example Convention Centre
format: in_person
event_type: conference
organizer: Example C++ Association
path: https://example.org/2027/
registration_url: https://example.org/2027/register/
source_url: https://example.org/2027/announcement/
source_name: ExampleConf
last_verified: 2026-09-07
Requirements
- Provide
ical_uid,title,start_date,event_type, andsource_url - Provide the canonical event URL, format, organizer, location, and a useful factual description when available
- Keep
ical_uidglobally unique and stable; use a readable slug and year followed by@cpp.social - Use ISO 8601
YYYY-MM-DDdates;end_dateis the final day of the event - Allowed
formatvalues arein_person,online, andhybrid - Allowed
event_typevalues areconference,meetup,committee, andworkshop - Omit
registration_urlorvenuewhen they are not known source_urlmust link directly to first-party evidence for the date and location- Set
statustotentativeorcancelledwhen applicable; otherwise omit it - Set
last_verifiedto the date on which the source was checked
Times are optional
Omit start_time and end_time for multi-day events or when times are not published. For a single-day event, you may add its published start and end times as quoted HH:MM values, with a timezone from IANA's list.
Check the current announcement
Only add an event once the organizer has published its date. Do not reuse a previous year's location, venue, or format without checking the current announcement.
Check the calendar
Run make build and inspect /events/ in the build output. Check multi-day boundaries, timezone labels, external links, and the generated calendar entry.
Contributor guide
Packages
Missing registry packages, metadata corrections, and package matching
Missing packages
The package search is built from supported package-manager registries. There is no hand-written package list in this repository.
If a package is missing from its registry, add it upstream first. If it exists upstream but is absent from cpp.social, open a content-correction issue with:
- The package manager and exact package ID
- The upstream registry or recipe URL
- The expected project homepage or repository
- The date on which you confirmed it was present upstream
Registry catalogs are generated
Do not add records directly to data/packages/conan.yaml, meson.yaml, spack.yaml, or another manager catalog.
Correct a metadata field
First fix the upstream registry when possible. For a local metadata fix, add a corrections entry to data/packages/overrides.yaml. These entries use PackageFieldCorrection in the schema:
corrections:
- package: vcpkg:example
field: homepage
operation: replace
value: https://example.org/
reason: Registry metadata still points to the retired project site
package is the exact ID from a registry catalog, such as vcpkg:fmt. field names a field on that package; add version to target one exact version instead. replace is the default operation; remove deletes a field, and add appends unique values to a list field. Explain the correction in reason and link to its source in the PR description.
See the override schema and correction code for the supported fields and behavior.
Group packages from the same project
Records for the same upstream project can be grouped under groups in data/packages/overrides.yaml. These entries use PackageGroupOverride, which has different fields from a metadata correction:
groups:
- id: example
name: Example
packages:
- conan:example
- vcpkg:example
reason: These recipes use the same upstream repository
Replace the example IDs with exact IDs from the registry catalogs. id, name, aliases, and reason are optional; packages is required.
Every ID in packages must already exist in the saved registry catalogs; a missing ID fails package publishing. Do not add placeholders for packages that are not yet available.
To keep two packages separate, add a never_merge record with those two IDs under packages and a reason.
preferences selects the source for a merged entry's summary or licenses. Its package is the entity ID from data/packages/entities.yaml; its source is a registry package ID belonging to that entity. The schema also accepts URL fields here, but the current browser build does not apply those preferences.
Package matching changes require updater tests.
Matching output is generated
Edit data/packages/overrides.yaml, not the generated entities.yaml or matches.yaml.
Check package changes
Run make package-publish to apply overrides to the saved catalogs and regenerate package identities and matches, then make build to update the browser data. Run make packages only when the registries need fetching again. For matching changes, also run make test. Check the affected package details and leave unrelated generated changes out of the PR.
Contributor guide
Code, UI, and docs
Templates, styles, browser code, updater changes, and documentation
Put the change in the right subtree
| Change | Location |
|---|---|
| Site content | content/ |
| Text for UI elements | ui/ |
| Site and page settings | config/ |
| Page-specific templates | templates/pages/ |
| Reusable UI components | templates/components/ |
| Top-level sections | templates/blocks/ |
| Client-side JS code | frontend/ |
| Stylesheets and static files | assets/ |
| Browser data generation | site_plugins/ |
| Metadata updater | updater/ |
| Contributor and maintainer documentation | docs/ |
UI changes
The primary CSS entrypoint is assets/static/style.css. All other stylesheets should be put in assets/static/styles, especially when that stylesheet is specific to some page or feature.
Keep the import order unless the change needs a different CSS cascade.
Check keyboard navigation, focus visibility, narrow layouts, dark mode, empty states, and long titles. Include screenshots or a short recording in the pull request for visible changes.
Updater changes
Network failures, empty responses, and malformed source data must not replace a valid dataset. Keep output deterministic and preserve provenance URLs and retrieval timestamps.
Do not mix a broad metadata refresh into an updater code change. Use small fixtures rather than live network calls in tests.
Documentation changes
The contributing page renders the files under docs/ in the order declared by content/contributing/_index.yaml. Create new documentation files under docs/ and register each one in the index with a title, stable anchor ID, description, and !include entry.
Run make build and inspect /contributing/ at desktop and mobile widths.
Maintainer guide
Content maintenance
Content review guidelines and manual update and override procedures
Review a content submission
- Confirm that the entry is C++-relevant, public, maintainable, and not duplicated.
- Check that it is in the file named by the contributor guide and matches a nearby record.
- Open the canonical URL, all secondary links, and the first-party verification source.
- Verify names, identifiers, dates, timezone, location, and factual claims.
- Check the hard constraints for the content type.
- (Optionally) Run
make buildand inspect the affected rendered page or request a preview in the PR (/previewcommand).
Some CI checks may run (e.g. for Discord we verify that links are active, non-expiring and not a vanity link), inspect their output.
Two approvals are required
A pull request that changes any file under content/ may be accepted only after approval from at least two current maintainers or curators listed on the About page.
Manually refresh metadata
Install the tools once with make install.
To update metadata for some specific new content, prefer running a narrow updater rather than make update-all.
| Curated change | Command |
|---|---|
| Blog source | make blogs ID=<source-id> |
| Book ISBN | make books ID=<isbn> |
| Community | make communities ID=<community-id> |
| Meetup calendar source | make events |
| YouTube channel | make youtube ID=<channel-id> |
| Package override only | make package-publish |
| Package registry sources | make packages |
These commands update data/. All fetch remote sources except make package-publish, which uses saved catalogs. make events refreshes every configured Meetup feed - it has no per-group ID option.
Content PRs may include related data/ changes, such as the initial ingest or a targeted refresh for a new blog or YouTube channel. Do not include metadata changes for unrelated entries.
For metadata patches, verify:
- Expected source IDs appear once and in the correct dataset
- Provenance names the URLs actually fetched
- Existing records were not removed because of a transient or empty response
- Sorting and serialization are stable
- No unrelated updater output is included
For updater code changes, run:
make check-format
make lint
make test
make build
Add overrides
- Manually added events go to
content/events/events.yaml, neverdata/events-imported.yaml - To suppress a fetched post or video, keep its
post_idorvideo_idand sethidden: trueindata/blogs/posts/<source-id>.yamlordata/youtube/videos/<channel-id>.yaml. Other fields may be removed from that hidden record - Put package corrections and merge decisions in
data/packages/overrides.yaml, never in generated manager catalogs - Apart from these overrides and hidden records, do not hand-edit fetched metadata, provenance, imported events, package entities, or match output
To remove registry records from package search, add a fully qualified manager ID to ignored or a stable, manager-qualified prefix to ignored_prefixes in data/packages/overrides.yaml. Make sure to do this for every affected package manager.
Run make package-publish, then make build, and confirm the primary package and its graph edges are absent.
Maintainer guide
Triage
Issue forms, types, and labels used to route repository work
To make long-term maintenance of the site easier, please make sure issues are tagged correctly.
We currently use only a small number of thematic labels, since the issue content generally makes it clear what kind of issue it is.
Issue types
| Type | Use |
|---|---|
Addition | New directory or calendar entry |
Correction | Existing content is stale or wrong |
Bug | Site or updater behavior is broken |
Feature | New feature suggestions |
Labels
| Label | Use |
|---|---|
community | Additions to the communities page |
event | Changes to the events page |
publication | Blogs, books, or YouTube channels |
resource | Resource or ecosystem listing |
ui | Templates, styles, UI configuration etc. |
updater | Metadata updater changes |
documentation | Improvements or additions to documentation |
duplicate | This issue or pull request already exists |
Admin guide
Site administration
Documentation for administration of the site and the Github organization
Hosting
The site is published at cpp.social through GitHub Pages from cppsocial/site. The deployment workflow also triggers a separate mirror repository.
To manually redeploy, rerun the build-deploy.yaml workflow.
Redeploying manually
Make sure to only ever run the build-deploy.yaml workflow on master. Deploying from other branches interferes with our metadata handling and may break the page.
Pull request previews
Pull request previews are opt-in because the preview builds code from the pull request. After reviewing the change, a repository collaborator with write access can comment /preview on the pull request. The workflow records the current pull request commit, refreshes only metadata selected by changed curated records, builds without repository secrets, and deploys the resulting artifact to https://pr-<number>--cppsocial.netlify.app.
The deploy uses the netlify-preview environment, never github-pages. On success, the workflow comments with the preview URL and Netlify CLI output.
Tokens & Mirror
The content of the primary site is mirrored onto cppsocial.github.io via Github actions. This is a separate repository (and just republishes the build artifacts from the primary repo) to prevent Github from redirecting to cpp.social.
To facilitate this, there's two PATs in place: - GH_IO_MIRROR gives cppsocial/site R/W access to actions in cppsocial/cppsocial.github.io. This is used to start the mirroring action after a successful site built. - GH_IO_SOURCE gives cppsocial/cppsocial.github.io read access to cppsocial/site's action artifacts
These might need updating every now and then.
Adding Maintainers/Curators
Checklist:
- Add new maintainer or curator to MAINTAINERS.md
- Invite them to the cppsocial organization here
- Add them to the appropriate teams (always add Maintainers to the Curator team as well!)
MAINTAINERS.md Changes
New maintainers/curators may add themselves to MAINTAINERS.md and are encouraged to do so.
If we instead open that PR for them, make sure they approve the PR before merging.