Skip to content

About

CRA FAQ

Resources

Code of conduct

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

cra.orcwg.org

Eleventy-based static site generator that creates a FAQ website for ORC WG's CRA Hub at cra.orcwg.org. The site consumes FAQ content from an external GitHub repository and generates a categorized FAQ website.

Development Setup

Prerequisites

  • Node.js
  • npm
  • poppler (optional): only needed to regenerate the Official CRA Guidance page and to run its end-to-end tests, which are skipped without it. Install it with brew install poppler on macOS or apt-get install poppler-utils on Debian/Ubuntu.

Installation

git clone https://github.com/orcwg/cra.orcwg.org.git
cd cra.orcwg.org
npm install

Build Commands

  • npm run serve - Start development server with live reload (no cache update)
  • npm run watch - Watch for file changes and rebuild (no cache update)
  • npm run build - Build the production site (no cache update)
  • npm test - Run the unit tests (Node's built-in test runner, tests in test/)
  • npm run update-cache - Update external content cache
  • npm run update-cache -- branch-name - Update the external content cache to use a different branch of the orcwg/cra-hub repositiory. Great for testing the build with unmerged pull requests.
  • npm run extract-guidance - Regenerate the Official CRA Guidance page (src/official-guidance.html), its API document (src/_data/official-guidance.json, served at /api/v0/documents/official-cra-guidance.json) and its figures (src/assets/images/official-guidance/) from the Commission's guidance PDF in src/_data/, verifying the extracted text against the PDF (requires poppler). The generated files are committed and shouldn't be edited by hand.
  • npm run extract-guidance -- --check - Confirm the committed Official CRA Guidance page matches the PDF, without writing anything (requires poppler).

Architecture

This is an Eleventy site that acts as a content processor and renderer for external FAQ content rather than managing content locally. The site uses a semantic HTML-first architecture with minimal component classes and CSS-only interactions.

Design Philosophy

  • Semantic HTML Elements: Uses native HTML5 elements (<details>, <summary>, <aside>, <article>) with minimal class names
  • CSS-Only Interactions: Accordions and animations use native browser features without JavaScript
  • Component Independence: Components are self-contained and don't rely on parent context
  • Modern CSS Features: Leverages color-mix(), :has(), :empty, grid template rows, and CSS Custom Properties
  • Accessibility First: Native HTML semantics provide built-in keyboard navigation and screen reader support

CSS Architecture

The site uses a hybrid approach combining semantic HTML selectors with light component classes:

Semantic Elements:

  • <header class="site-header"> - Site header with flexbox layout
  • <nav class="site-nav"> - Navigation menu
  • <main> - Main content area
  • <footer> - Site footer with grid layout
  • <aside> - Warnings and disclaimers (styled automatically)
  • <blockquote> - Minimal italic quotes

Component Classes:

  • .section-card - White boxes for FAQ content and list sections
  • .section-intro - Light gray introductory text boxes
  • .card-grid - Responsive card grids (with .half-width variant)
  • .link-list - FAQ link lists with hover states
  • .faq-accordion-item - Native <details> with CSS animations

Utility Classes:

  • .badge - Status indicators with semantic color variants
  • .info, .success, .warning, .danger, .important - Semantic colors using color-mix()

Content Flow

  1. External Content - FAQ content is maintained in the orcwg/cra-hub repository
  2. Cache Update - The update-cache.sh script clones/updates external content into _cache/faq/
  3. Data Processing - src/_data/data.js orchestrates the complete data pipeline:
    • Text Processing: Markdown to plain text conversion for titles and summaries
    • File Operations: Parsing markdown files with frontmatter
    • FAQ Processing: Extract questions, answers, and metadata from FAQ markdown files
    • Guidance Request Processing: Parse pending guidance request documents
    • FAQ List Processing: Load and normalize README.yml files from FAQ subdirectories
    • Authors Processing: Load and merge AUTHORS.md from FAQ repository and CONTRIBUTORS.md from website repository
    • Cross-referencing: Cross-reference FAQs with guidance requests and lists
    • Permalink Generation: URLs computed once in data layer, not reconstructed in templates
  4. Template Rendering - Nunjucks templates consume processed data using semantic HTML components
  5. Site Generation - Final site is output to _site/ for deployment

Content Types

  • FAQs: Questions and answers with status tracking (draft, approved, pending guidance)
  • Guidance Requests: Items awaiting EU Commission clarification

Data Pipeline Details

The data processing pipeline in src/_data/data.js is organized into modular sections:

  1. Utility Functions

    • Text processing: Convert markdown to plain text for page titles
    • File operations: Parse markdown files with frontmatter using gray-matter
  2. Content Processors

    • FAQs: Extract questions from # headings, answers from subsequent content, status badges, and GitHub edit links
    • Guidance Requests: Parse pending guidance documents, extract titles and "Guidance Needed" sections
    • FAQ Lists: Load README.yml files from FAQ subdirectories, normalize FAQ references
    • Authors: Load and process AUTHORS.md from FAQ repository and CONTRIBUTORS.md from website repository, extract name lists for acknowledgements
  3. Relationship Building

    • Link FAQs to their related guidance requests via guidance-id frontmatter field
    • Bidirectionally connect lists with their referenced FAQs
    • Each FAQ knows which lists include it; each list knows its FAQs

FAQ Lists

  • README.yml files in FAQ subdirectories define FAQ collections
  • Each README.yml file creates a new list page at /lists/{category}/
  • Lists reference FAQs by filename (short form) or category/filename (long form)
  • FAQ references are automatically normalized to category/filename format
  • Lists maintain bidirectional links with FAQs for navigation
  • Lists are displayed using native HTML <details> accordions for compact navigation

Official FAQs (European Commission and ENISA)

Besides the community FAQs from orcwg/cra-hub, the site integrates official EU content:

  • European Commission "FAQs on the Cyber Resilience Act" (PDF) - parsed by src/_data/parse-official-faqs.js from a Markdown conversion of the PDF stored in src/_data/, and published at /faq/official/. Section metadata (slugs, icons, descriptions) lives in official-faqs-config-lists.json, per-FAQ metadata (related issues) in official-faqs-config-faqs.json.
  • European Commission "Cyber Resilience Act - Questions and Answers" (web page) - fetched at build time from the Commission's press corner API by fetchAndAddECFaqs in src/_data/data.js, parsed by src/_data/utils/ec-qanda-parser.js, and exposed as the cra-basics dynamic list.
  • ENISA "Single Reporting Platform (SRP) - Frequently Asked Questions" (web page) - fetched at build time from ENISA's FAQ page by fetchAndAddSrpFaqs in src/_data/data.js, parsed by src/_data/utils/srp-faq-parser.js, and exposed as the srp dynamic list. The <dl class="ckeditor-accordion"> question/answer pairs and the "Updated: ..." date are extracted from the page. Only questions ENISA marks "[UPDATED]" get that update date (the marker itself is removed from the question). The page doesn't expose its publication date, so it is set by SRP_FAQ_PUBLISHED in data.js, based on press coverage of the page's launch. FAQ ids follow ENISA's question numbers (srp/faq_<n>), and references to CRA articles ("Art. 14(7)"), other SRP FAQs ("FAQ 21") or sections of the Commission's FAQ ("subsection 5.1") are linked by linkSrpCrossReferences in the same module.

Answers from both web pages are converted from HTML to Markdown (src/_data/utils/html-to-markdown.js, using turndown) before link resolution. The link resolver inserts Markdown links, which markdown-it would otherwise leave unrendered inside raw HTML blocks. The link resolver never inserts a link inside the text of an existing link.

Both pages are fetched by fetchWithFallback (src/_data/utils/fetch-with-fallback.js), which keeps a snapshot of the last page that was fetched and parsed successfully in _snapshots/ (not committed). When a page cannot be fetched within 30 seconds or its structure changes, the build falls back to that snapshot, logs a warning and, in GitHub Actions, adds a warning annotation and a job summary entry so that the problem doesn't go unnoticed. Without a snapshot, the build fails, so that a broken site is never deployed. Set OFFICIAL_FAQ_FETCH=strict to disable the fallback (e.g. to check that a parser still matches the live page). The parsers are covered by unit tests using excerpts of both pages (test/fixtures/); when a page structure changes, update the fixture along with the parser.

Dynamic Lists

The system automatically generates special lists based on FAQ properties. These are configured in the DYNAMIC_LISTS array in src/_data/data.js and are automatically added to the Topics page.

Available Dynamic Lists:

  • new - FAQs created within the last 30 days (sorted newest first)
  • recently-updated - FAQs updated within the last 14 days (sorted by update date)
  • cra-basics - the European Commission's "Cyber Resilience Act - Questions and Answers" (fetched at build time)
  • srp - ENISA's Single Reporting Platform FAQs (fetched at build time)
  • unlisted - FAQs not yet assigned to any curated list

Configuration Properties:

  • id - Unique identifier for the list
  • title, icon, description - Display metadata
  • emptyMsg - Message shown when the list has no items
  • inclusionFilter - Function determining which FAQs to include
  • sortChildren - Optional function to sort FAQs in the list
  • hideInAllFaqsFilter - Function controlling visibility in "All FAQs" view
  • hideInTopicsFilter - Function controlling visibility in topics view
  • insertAt - Position in root list ('top', 'bottom' or 'end')

How Dynamic Lists Work:

Dynamic lists are generated at build time by the createAndInsertDynamicLists() function:

  1. Each dynamic list is created from its configuration
  2. FAQs matching the inclusionFilter are added as children
  3. Bidirectional parent-child relationships are established
  4. Children are sorted using sortChildren if configured
  5. Metadata (timestamps, visibility) is calculated from children
  6. Lists are inserted into the root list based on insertAt:
    • 'top' lists appear at the beginning (in array order)
    • 'bottom' lists appear at the end (in array order)
    • 'end' lists appear after all other lists, including the official FAQs (in array order)

Dynamic lists are fully automatic and cannot be manually referenced in YAML files.

FAQ Components

The site uses two reusable FAQ components with minimal markup:

  • faq-list-simple.njk - Simple link lists using semantic <ul class="link-list">

    • Displays FAQ questions as clickable links
    • Shows status badges when present
    • Used in main FAQ list page and list indexes
  • faq-accordion.njk - Native <details> accordions with CSS-only animations

    • Uses HTML5 <details> and <summary> elements
    • Exclusive accordion behavior via name="faq-accordion" attribute
    • Smooth open/close animations using CSS grid template rows
    • No JavaScript required for accordion functionality
    • Used in lists for inline FAQ browsing

Component Structure

Components follow a consistent data pattern:

{% set componentData = {
  title: "Section Title",
  description: "Optional description",
  icon: "๐Ÿš€",
  faqs: arrayOfFaqObjects
} %}
{% include "components/faq-accordion.njk" %}

Site Configuration

  • Global site settings in src/_data/site.json
  • Navigation menu configuration (used in header, homepage cards, and footer); entries with hideFromNav: true are shown on the homepage only
  • List ordering controlled via listOrder array
  • Footer content structured as sections with titles and lists

External Dependencies

Primary Content Source

  • Repository: orcwg/cra-hub
  • Purpose: Contains all FAQ content and guidance requests in markdown format
  • Update: Automatically pulled during builds via update-cache.sh

Technology Stack

  • Static Site Generator: Eleventy 3.x
  • Template Engine: Nunjucks
  • Markdown Processing: markdown-it with GitHub alerts plugin
  • Diagram Support: Mermaid.js (for rendering code blocks with language-mermaid)
  • CSS Architecture:
    • Semantic HTML-first approach with minimal utility classes
    • CSS Custom Properties for theming
    • Modern CSS features: color-mix(), :has(), :empty, grid template rows
    • CSS-only accordions using native <details> elements
  • JavaScript: Minimal - only used for Mermaid diagram initialization (src/assets/js/site.js)

Development Resources

  • Style Guide: Visit /style-guide/ to preview all components, design patterns, and semantic HTML structures used throughout the site. The style guide is excluded from collections but available during development.

Key Components

  • .section-card - White content boxes with shadow and border
  • .section-intro - Light gray intro/description sections
  • .card-grid - Responsive grid layout for navigation cards
  • .card - Individual cards with hover effects and animated arrows
  • .link-list - Styled FAQ link lists with badge support
  • .faq-accordion-item - Native <details> elements with CSS animations
  • Semantic color utilities: .info, .success, .warning, .danger, .important

Deployment

The site is deployed to GitHub Pages at cra.orcwg.org with automatic updates triggered by changes to the content repository.

Deployment Architecture

This repository (cra.orcwg.org) acts as the website generator, while orcwg/cra-hub is the content source. The two repositories work together through an automated workflow:

flowchart TD
    A[orcwg/cra-hub<br/>Content Repository]
    B[Push to main]
    C[orcwg/cra.orcwg.org<br/>Website Repository]
    D[Push to main]
    E[Build and Deploy<br/>GitHub Actions]
    F[cra.orcwg.org<br/>Live Website]
    G[Daily schedule<br/>04:23 UTC]

    A -->|Update content source| B
    B -->|Webhook trigger| E
    C -->|Update  website generator| D
    D -->|Direct trigger| E
    G -->|Cron trigger| E
    E -->|Deploy to Pages| F

    style A fill:#e1f5ff
    style C fill:#fff4e1
    style E fill:#ffe1e1
    style F fill:#e8f5e9
Loading

Automatic Update Workflow

The website automatically rebuilds and deploys through three triggers:

  1. Content updates - When content is pushed to main in orcwg/cra-hub
  2. Website changes - When code is pushed to main in this repository
  3. Daily schedule - Every day at 04:23 UTC, to pick up changes to the official FAQ pages fetched at build time

Trigger 1: Content Repository Updates

When content is pushed to the main branch of orcwg/cra-hub, a GitHub Action workflow sends a repository_dispatch webhook to trigger a rebuild of this website.

How it works:

  1. The workflow listens for pushes to the main branch in cra-hub
  2. It sends a repository_dispatch event with type cra-hub-update to this repository via the GitHub API using a GitHub token (WEBSITE_DISPATCH_TOKEN secret stored in cra-hub).

Note

The WEBSITE_DISPATCH_TOKEN must be renewed annually and requires the permissions listed below.

  • Repository access: Only orcwg/cra.orcwg.org
  • Contents: Read & Write (required for repository_dispatch)
  • Metadata: Read (auto-selected)

Trigger 2: Website Repository Updates

When changes are pushed to the main branch of this repository (template updates, styling changes, configuration), the deployment workflow runs directly without needing a webhook.

Trigger 3: Daily Schedule

The European Commission's "Questions and Answers" and ENISA's SRP FAQs are fetched from their web pages at build time, so the deployment workflow also runs on a daily schedule (04:23 UTC). Scheduled runs are resistant to problems with those pages: the last successfully parsed copy of each page is kept in the GitHub Actions cache between runs (actions/cache/restore and actions/cache/save around the build step) and used as a fallback, as described in Official FAQs. A run that had to fall back shows a warning annotation and a job summary entry. If a page is unavailable and there is no cached snapshot yet, the build fails and the previous deployment stays online.

Note

GitHub only runs scheduled workflows from the default branch, and automatically disables them in repositories with no activity for 60 days (a notification email is sent; the workflow can be re-enabled from the Actions tab). Cache entries are evicted after 7 days without access, which the daily run prevents.

All triggers execute the same deployment workflow:

  1. Pulls latest content from cra-hub via update-cache.sh
  2. Processes FAQ data through the Eleventy data pipeline
  3. Generates static HTML pages
  4. Publishes to GitHub Pages at https://cra.orcwg.org

Manual Deployment

For manual deployments or testing, the build process:

  1. Runs update-cache.sh to clone/update cra-hub into _cache/
  2. Processes FAQ data from cloned repository via src/_data/data.js
  3. Generates static HTML pages using Eleventy
  4. Outputs complete site to _site/ directory

Local Development

When developing locally:

# Start development server (updates cache and serves with live reload)
npm run serve

# Build production site (updates cache and builds)
npm run build

# Manually update content cache only
npm run update-cache

The update-cache.sh script clones or pulls the latest cra-hub content into _cache/

License

This project is licensed under the terms of the Apache License Version 2.0.

SPDX-License-Identifier: Apache-2.0

About

CRA FAQ

Resources

Code of conduct

Stars

0 stars

Watchers

1 watching

Forks

Contributors

Languages