This guide is the operational reference for maintainers, contributors, and anyone running a fork of h5bp/Front-end-Developer-Interview-Questions. The repository is a static documentation site published at h5bp.org/Front-end-Developer-Interview-Questions that catalogues a curated bank of front-end interview questions in English plus 34 community-contributed translations. The site is built with Eleventy 2.0.1 (no Vue, no React, no client-side framework — it is intentionally a near-zero-JS static site) and is one of the longest-running open-source projects in the H5BP family, with contributions from over 100 developers since 2009.
The User Guide for end users covers cloning, running the dev server, browsing categories, and the contribute-a-question workflow. This Admin Guide picks up where that ends — it documents the build pipeline, every config knob in the Eleventy file, deployment patterns for static-site hosts, the file-discovery and i18n model that makes new translations a one-data-row drop, performance and security posture for a static docs site, and the integration surface for downstream tooling that wants to consume the question corpus.
1. Architecture Overview
The repository is a content-first Eleventy static site. Question content lives in plain Markdown files; presentation is provided by a small set of Nunjucks templates; the config/eleventy.config.js file wires Markdown rendering, asset minification, and the i18n plugin together. There is no application server, no database, and no API. The only runtime artifact is a static _site/ directory of HTML/CSS/JS/images that any static host can serve.
Conceptual diagram
+-----------------------------------------------------------------------+
| SOURCE (src/) |
| |
| src/_data/ src/questions/ src/translations/ |
| +------------------+ +-----------------+ +------------------+ |
| | site.json | | general-*.md | | arabic/README.md | |
| | questions.json | | html-*.md | | bengali/... | |
| | translations.json| | css-*.md | | ... (34 langs) | |
| | contributors.json| | javascript-*.md | | _template/... | |
| | maintainers.json | | testing-*.md | +------------------+ |
| | og.json | | performance-*.md| |
| | helpers.js | | network-*.md | src/_includes/ |
| +------------------+ | coding-*.md | +------------------+ |
| | fun-questions.md| | layouts/*.njk | |
| src/_includes/assets/ +-----------------+ | components/*.njk | |
| +------------------+ | assets/css/*.css | |
| | css/*.css | Nunjucks templates | assets/js/app.js | |
| | js/app.js | (.njk) +------------------+ |
| +------------------+ |
+-----------------------------------------------------------------------+
|
| npm run build
v
+-----------------------------------------------------------------------+
| BUILD (Eleventy 2.0.1, config/eleventy.config.js) |
| |
| +---------------------+ +-----------------------+ |
| | EleventyI18nPlugin | | plugin-syntaxhighlight| |
| | (defaultLanguage:en)| | (Prism) | |
| +---------------------+ +-----------------------+ |
| |
| +---------------------+ +-----------------------+ |
| | markdown-it v14 | | filters: | |
| | + markdown-it-anchor| | cssmin (clean-css) | |
| | (level 2, permalink | | jsmin (uglify-es) | |
| | before, no symbol) | | htmlmin (transform) | |
| +---------------------+ | markdownify | |
| | strip_html | |
| | html_date_string | |
| | github_permalink | |
| | permalink | |
| +-----------------------+ |
| |
| Passthrough copy: src/assets -> _site/assets |
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| OUTPUT (_site/) |
| |
| index.html, about/, questions/<category>/index.html (x9), |
| translations/<lang>/index.html (x34), 404.html, sitemap.xml, |
| assets/images/*.png|.svg |
| |
| Deployed to GitHub Pages / Netlify / Vercel / any static host. |
+-----------------------------------------------------------------------+
Component inventory
| Component | Path | Role |
|---|---|---|
| Build config | config/eleventy.config.js |
Single Eleventy config — registers plugins, filters, transforms, and the input/output directory layout. |
| Question content | src/questions/*.md |
Nine canonical English Markdown files (one per category). |
| Translation content | src/translations/<lang>/README.md |
34 locale folders, each containing a README.md mirroring the English master. |
| Template starter | src/translations/_template/README.md |
Skeleton file used by translators when seeding a new locale. |
| Layouts | src/_includes/layouts/{default,page}.njk |
Two Nunjucks layouts — default is the wrapper, page is used by translation and category pages. |
| Components | src/_includes/components/{footer,navigation}.njk |
Partials included by layouts for global chrome. |
| Page templates | src/{index,about,translations,404,sitemap.xml}.njk |
Top-level Nunjucks pages compiled to HTML/XML. |
| Stylesheets | src/_includes/assets/css/*.css |
Hand-authored CSS — defaults, variables, header, navigation, content, footer, prism. |
| Client JS | src/_includes/assets/js/app.js |
Twenty-six lines of vanilla JS for the mobile nav toggle and resize handling. |
| Global data | src/_data/*.json and src/_data/helpers.js |
Eleventy data cascade — the source of truth for category lists, translations, contributors, maintainers, OG meta. |
| Static assets | src/assets/images/* |
Favicons, OG image, background, gradient SVG — copied verbatim by addPassthroughCopy. |
| Top-level metadata | README.md, CONTRIBUTORS.md, LICENSE.md, .editorconfig, .gitignore, .all-contributorsrc |
Repo-root files — license is MIT, contributor manifest is regenerated by the all-contributors CLI. |
Technology stack
| Layer | Technology | Pinned version |
|---|---|---|
| Static site generator | Eleventy | ^2.0.1 |
| Templating | Nunjucks (built into Eleventy) | bundled with @11ty/eleventy |
| Markdown engine | markdown-it |
^14.1.0 |
| Markdown anchors | markdown-it-anchor |
^8.6.7 |
| Syntax highlighting | @11ty/eleventy-plugin-syntaxhighlight (PrismJS) |
^5.0.0 |
| Internationalization | EleventyI18nPlugin (built into @11ty/eleventy) |
bundled |
| CSS minification | clean-css |
^5.3.3 |
| JS minification | uglify-es |
^3.3.9 |
| HTML minification | html-minifier |
^4.0.0 |
| Date helper | luxon |
^3.4.4 |
| Contributor tooling | all-contributors-cli |
^6.26.1 |
| Lockfile | package-lock.json (lockfileVersion 3) |
committed |
| Resolution override | axios pinned via resolutions |
0.18.1 |
Note
The
resolutionsblock inpackage.jsonforces a transitive dependency override onaxios@0.18.1. This pre-dates modern npmoverridessyntax — it is a yarn-style hint. Modern npm users should be aware thatnpm installwill respect the lockfile version directly; the resolution is a no-op safety net unless you regenerate the lockfile under yarn.
Data flow
- Eleventy reads every file under
src/whose extension matchestemplateFormats: ['njk', 'md', 'html']. - For each file, the data cascade pulls in everything under
src/_data/— the per-category list, the translation list, the contributors map, and so on — making it available as global template variables. - Markdown files are rendered through the custom
markdown-itpipeline (with anchor injection at H2 only, permalinks placed before the heading text with an empty symbol — this produces clickable left-margin link anchors). - Nunjucks layouts consume the rendered Markdown via the
layoutfront-matter key. The two layouts aredefault.njk(the master shell) andpage.njk(used by translation pages and the about page). - The
htmlmintransform runs on every.htmloutput to collapse whitespace and strip comments. Thecssminandjsminfilters minify any inlined CSS/JS that templates pipe through them. addPassthroughCopy('src/assets')copies images and other binary assets verbatim into_site/assets/without templating.- The final
_site/directory is what gets deployed.
Note
Every page on the site is fully pre-rendered at build time. There is no client-side hydration, no fetch-on-load, and no runtime template re-execution. This is the architectural reason the site loads instantly on slow connections — and the reason it has no server-side attack surface.
2. System Requirements
The build chain is intentionally minimal. Anything that runs Node.js 14 or newer can build and serve the site.
Runtime requirements
| Requirement | Minimum | Recommended | Notes |
|---|---|---|---|
| Node.js | 14.x | 20.x or 22.x LTS | Eleventy 2.0.1 declares engines.node >=14. Issue #932 tracks migrating GitHub Actions runners to Node 22/24. |
| Package manager | npm 6+ |
npm bundled with Node 20+ |
The committed package-lock.json is lockfileVersion: 3, written by npm 7+; older npm clients will rewrite it on install. |
| Disk space | ~250 MB | ~400 MB | node_modules is the bulk of this; the source tree itself is under 4 MB. |
| Memory | 512 MB free | 1 GB+ | The build is single-threaded and finishes in seconds on any modern machine. |
| OS | Linux, macOS, Windows | Linux/macOS | All transitive dependencies have prebuilt binaries; Windows works for development. |
Build-time tooling
The devDependencies block of package.json is the complete list of non-transitive build-time requirements:
"devDependencies": {
"@11ty/eleventy": "^2.0.1",
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0",
"all-contributors-cli": "^6.26.1",
"clean-css": "^5.3.3",
"html-minifier": "^4.0.0",
"luxon": "^3.4.4",
"markdown-it": "^14.1.0",
"markdown-it-anchor": "^8.6.7",
"uglify-es": "^3.3.9"
}
There are no dependencies — the site has zero runtime dependencies because there is no runtime. Every package above is needed only during npm run build or npm start.
Network requirements
The build itself is fully offline once node_modules is installed. Initial npm install requires access to the npm registry. No external API calls are made during build or at runtime.
3. Configuration Reference
Configuration is split across three places: package.json for npm scripts and dependency versions, config/eleventy.config.js for the build pipeline, and src/_data/*.json for content metadata. There is no .env, no YAML config, no runtime settings.
package.json scripts
Every script defined in the repo:
| Script | Command | Purpose |
|---|---|---|
build |
eleventy --config=config/eleventy.config.js --pathprefix='Front-end-Developer-Interview-Questions/' |
Production build. Writes _site/. The --pathprefix is what makes the deployed URLs work under h5bp.org/Front-end-Developer-Interview-Questions/.... |
start |
eleventy --config=config/eleventy.config.js --serve --port 9090 --quiet |
Dev server with live reload on port 9090. The --quiet flag suppresses Eleventy's per-file build log lines. |
contributors:add |
all-contributors add |
Interactive prompt to add a contributor to .all-contributorsrc. |
contributors:generate |
all-contributors generate && cp .all-contributorsrc ./src/_data/contributors.json |
Regenerates the README contributor table and syncs .all-contributorsrc into the data cascade so templates can render it. |
Important
The
--pathprefixargument in thebuildscript hard-codes the deployed URL prefix. If you fork this repository and deploy to a different path (or to the root of your own domain), you must edit thebuildscript. Forgetting to do so produces a site whose internal links 404 on every page — the symptom is "every link looks right but every click fails." See section 4 for fork-deployment patterns that get this right.
config/eleventy.config.js — the pipeline
The Eleventy config is the only file that exports build behavior. Every plugin, filter, transform, and directory mapping lives here. The verbatim source is short enough to summarise inline.
Plugins registered
| Plugin | Configuration |
|---|---|
@11ty/eleventy-plugin-syntaxhighlight |
Default settings (Prism wrapper). Used inside fenced code blocks in question files. |
EleventyI18nPlugin |
{ defaultLanguage: 'en' } — wires up the getLocaleUrl and locale_url helpers used by the language switcher. |
Markdown library override
const markdown = require('markdown-it')({
html: true,
breaks: true,
linkify: true,
typographer: true
}).use(require('markdown-it-anchor'), {
level: [2],
permalink: true,
permalinkBefore: true,
permalinkSymbol: ''
})
| Option | Value | Effect |
|---|---|---|
html |
true |
Allows raw HTML inside Markdown (translation files lean on this for <div dir="rtl"> wrappers). |
breaks |
true |
Single newlines render as <br> (consequential for translators — line-broken paragraphs become visually split). |
linkify |
true |
Bare URLs are auto-linked. |
typographer |
true |
Smart quotes and dash substitutions. |
markdown-it-anchor.level |
[2] |
Only <h2> headings get anchor IDs and permalinks. H3 and H4 stay plain. |
permalinkBefore |
true |
The link is rendered before the heading text, producing the "left-margin link" effect. |
permalinkSymbol |
'' |
The link itself contains no visible symbol — styling is purely CSS-driven. |
Filters registered
| Filter | Signature | Implementation |
|---|---|---|
cssmin |
(code) => string |
new CleanCSS({}).minify(code).styles |
jsmin |
(code) => string |
UglifyJS.minify(code); on error, logs 'UglifyJS error:' and returns the original code unchanged. |
markdownify |
(str) => string |
markdown.render(str) — full block-level Markdown rendering. |
markdownify_inline |
(str) => string |
markdown.renderInline(str) — inline-only (no block wrapping). |
strip_html |
(str) => string |
Removes <script>, <style>, HTML comments, and any tag via a regex over <script.*?</script>, <!--.*?-->, <style.*?</style>, and <.*?> patterns. |
html_date_string |
(dateObj) => string |
Returns DateTime.fromJSDate(dateObj).toFormat('yyyy-LL-dd') — for sitemap and meta tags. |
github_permalink |
(str) => string |
Replaces /index.html with .md — turns site URLs into GitHub source URLs. |
permalink |
(str) => string |
Strips .html suffixes for clean URL display. |
Transforms
A single htmlmin transform runs on every .html output:
eleventyConfig.addTransform('htmlmin', (content, outputPath) => {
if (outputPath.indexOf('.html') > -1) {
return htmlmin.minify(content, {
useShortDoctype: true,
removeComments: true,
collapseWhitespace: true
})
}
return content
})
| Minifier option | Value | Effect |
|---|---|---|
useShortDoctype |
true |
Emits <!DOCTYPE html> instead of the full XHTML doctype. |
removeComments |
true |
Strips HTML comments from the build output. |
collapseWhitespace |
true |
Collapses runs of whitespace to a single space. |
Directory mapping
return {
templateFormats: ['njk', 'md', 'html'],
dir: {
input: 'src',
includes: '_includes',
data: '_data',
output: '_site'
},
markdownTemplateEngine: 'njk',
htmlTemplateEngine: 'njk',
dataTemplateEngine: 'njk',
passthroughFileCopy: true
}
| Setting | Value | Why it matters |
|---|---|---|
templateFormats |
['njk', 'md', 'html'] |
Eleventy ignores all other extensions in src/. |
dir.input |
'src' |
Source root. |
dir.includes |
'_includes' (relative to dir.input) |
Layouts and partials. |
dir.data |
'_data' (relative to dir.input) |
Global data cascade. |
dir.output |
'_site' |
Build output. Deletable safely — it is regenerated every build. |
markdownTemplateEngine |
'njk' |
Markdown files are first run through Nunjucks (so they can use template variables) before Markdown rendering. |
passthroughFileCopy |
true |
Combined with addPassthroughCopy('src/assets'), this copies binary assets without templating. |
Content data files (src/_data/)
These JSON files drive what the site shows. Editing them rebuilds the navigation, the language switcher, and the contributor list without touching templates.
| File | Schema | Purpose |
|---|---|---|
site.json |
{ title, url, description, keywords, language, twitter } |
Global site metadata. The url value (https://h5bp.org/Front-end-Developer-Interview-Questions/) is the canonical production URL. |
questions.json |
{ all: [{ abbreviation, title, url, external }, ...] } |
The category nav. Ten entries: nine internal (general, html, css, javascript, testing, performance, network, coding, fun) plus the external A11y link to Scott O'Hara's accessibility-questions repo. |
translations.json |
{ all: [{ language, url }, ...] } |
The language switcher. 34 entries, ordered roughly by locale name. The url is a relative path under /translations/. |
contributors.json |
Generated by all-contributors generate from .all-contributorsrc |
The full contributor manifest used to render the README contributor table. Includes per-person login, name, avatar_url, profile, and a contributions array (doc, code, translation, infra, review, ideas, question, talk, maintenance, content, design). imageSize: 120, contributorsPerLine: 6. |
maintainers.json |
{ all: [...], legacy: [...] } |
Active and historical maintainer roster (avatar, name, website, GitHub, twitter). |
og.json |
{ all: [...] } |
The "original gangsters" — the 14 founding contributors recognised on the about page. |
helpers.js |
{ currentYear() } |
A single helper exposing the current year for the footer copyright. |
Other configuration files
| File | Purpose |
|---|---|
.editorconfig |
Editor-agnostic formatting rules (indentation, line endings) — ensures contributors across editors produce consistent diffs. |
.gitignore |
Excludes node_modules/, _site/, and editor scratch files from version control. |
.all-contributorsrc |
Configuration for the all-contributors-cli tool: project name Front-end-Developer-Interview-Questions, owner h5bp, image size 120, contributors-per-line 6, files target list ["CONTRIBUTORS.md", "README.md"], repo type github, commit convention none. |
4. Deployment & Scaling
The site is a fully static bundle. Deployment is "copy _site/ to a web host." That said, several common patterns exist; below covers each.
Build output
npm run build produces a complete _site/ directory. The structure mirrors the URL space:
_site/
index.html <- src/index.njk
about/index.html <- src/about.njk
404.html <- src/404.njk
sitemap.xml <- src/sitemap.xml.njk
translations/index.html <- src/translations.njk
translations/<lang>/index.html <- src/translations/<lang>/README.md (x34)
questions/<category>-questions/index.html <- src/questions/<category>-questions.md (x9)
assets/images/* <- src/assets/images/* (passthrough)
assets/css/*.css <- inlined or piped through cssmin
assets/js/app.js <- piped through jsmin
The bundle has no symbolic links and no executables — every file is a plain HTML/CSS/JS/image.
GitHub Pages (the canonical deploy)
The production site at h5bp.org/Front-end-Developer-Interview-Questions/ is served from GitHub Pages. The --pathprefix='Front-end-Developer-Interview-Questions/' argument in the build script is what makes URL paths line up with the GitHub Pages subpath.
A typical GitHub Actions workflow for this deploy:
name: Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-pages-artifact@v3
with:
path: _site
deploy:
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment
Tip
Issue #932 tracks migrating the project's existing CI to Node 22 or 24 — older runner images like
actions/setup-node@v3and Ubuntu 20.04 are deprecated. When you fork, start atactions/setup-node@v4andnode-version: '20'(or newer) to avoid runner-deprecation warnings on day one.
Netlify
Drop a netlify.toml at the repo root:
[build]
command = "npm run build"
publish = "_site"
[build.environment]
NODE_VERSION = "20"
If you deploy to a custom domain root (not a Netlify subpath), remove the --pathprefix from the build script first or override the command to eleventy --config=config/eleventy.config.js.
Vercel
vercel.json:
{
"buildCommand": "npm run build",
"outputDirectory": "_site",
"installCommand": "npm ci"
}
Self-hosted (any nginx/Apache)
npm ci
npm run build
rsync -av --delete _site/ user@host:/var/www/interview-questions/
For nginx, a minimal server block:
server {
listen 80;
server_name example.com;
root /var/www/interview-questions;
index index.html;
location / {
try_files $uri $uri/ $uri.html =404;
}
error_page 404 /404.html;
}
Scaling
There is nothing to scale at runtime — every visitor is served the same pre-rendered HTML. CDNs (Cloudflare, Fastly, the GitHub Pages CDN) cache the bundle indefinitely until a new build is pushed. Build times grow linearly with question and translation count, but the current 84-file repo builds in single-digit seconds.
Note
Because every translation is just another
README.mdconsumed by the same i18n plugin, scaling the site to support more languages does not change the build pipeline at all — see section 5 for the add-a-language runbook.
5. Operations
This section is the runbook for the four most common maintainer operations: adding a question, adding a translation, running the dev loop, and publishing.
Operating the local dev server
npm install
npm start
The dev server runs at http://localhost:9090/Front-end-Developer-Interview-Questions/ (note the path prefix). Saving any source file triggers a rebuild and a browser reload.
Tip
The
startscript passes--quietto suppress per-file build chatter. To debug template compilation (template-not-found errors are a common friction point), runnpx eleventy --config=config/eleventy.config.js --serve --port 9090directly so you see the per-file log lines.
Add a new question
- Open
src/questions/<category>-questions.md. Each category file is a Markdown list of questions under H2 section headings. - Append the question to the appropriate H3 sub-section (or add a new H3 if it is a new sub-topic). Use the same prose style as nearby questions — short, declarative, no answer.
- Save. The dev server (
npm start) hot-reloads. - Open a PR. Issue #935 is the open content audit — coordinate with that conversation if you are adding or removing significant material.
Add a new translation locale
- Copy
src/translations/_template/README.mdtosrc/translations/<new-language>/README.md. - Edit the front matter:
--- title: <Localized title> layout: layouts/page.njk permalink: /translations/<new-language>/index.html lang: <BCP 47 code> rtl: true # only for RTL languages — omit otherwise --- - Add an entry to
src/_data/translations.json:{ "language": "<Display Name>", "url": "<new-language>/" } - Translate the question content in the body. Keep the H2/H3 structure identical to the English master so that the i18n plugin can pair anchors across languages.
- Run
npm run buildand verify the translation appears in the language switcher and renders correctly. - Issue #870 is the open meta-issue for translation-process documentation — link your PR there if your workflow surfaces gotchas worth recording.
Update an existing translation
Updating a stale translation is the most common contribution after a major English edit. The open issues at the time of writing track exactly this:
| Issue | Translation | Status |
|---|---|---|
| #827 | Italian | Open |
| #812 | Burmese | Open |
| #828 | Japanese | Open |
| #834 | Russian | Open |
| #840 | Turkish | Open |
| #853 | Hindi (CSR/SSR question only) | Open |
The diff workflow:
- Open
src/questions/general-questions.md(and the other eight English files) in one pane. - Open
src/translations/<lang>/README.mdin another. - Walk top-to-bottom and add any English-only items as translated counterparts.
- Preserve the
<a name='...'>anchor markup that older translations use — those anchors are referenced from the in-page table of contents and breaking them produces visibly broken sidebar links.
Add a new question category
If a category does not yet exist (e.g., a new Security or Build Tools topic):
- Create
src/questions/<new-category>-questions.md, mirroring the structure ofgeneral-questions.md. - Add an entry to
src/_data/questions.json:{ "abbreviation": "<Short label>", "title": "<Full Title> Questions", "url": "/questions/<new-category>-questions/", "external": false } - The navigation rebuilds automatically.
Note
The category list in
questions.jsonincludes one entry with"external": true— the Accessibility (A11y) row points to Scott O'Hara's external accessibility-interview-questions repo. This is the only outbound nav item; if you add another, follow the sameexternal: trueflag pattern so the UI can render it differently from in-site links.
Update the contributors table
The H5BP family of repos uses the all-contributors bot and CLI:
npm run contributors:add -- <github-login> doc,code,translation
npm run contributors:generate
The contributors:generate script regenerates the README contributor block AND copies .all-contributorsrc into src/_data/contributors.json so the website renders the same data. Commit both files together.
Publish (cut a release)
The site has no semver releases — package.json is pinned at 1.0.0 and stays there. "Releasing" means merging to main and letting the deploy workflow ship to GitHub Pages.
Tip
Tag pre-merge milestones (e.g.,
2026-q2-translation-sync) for archival — there is no automated changelog, so tags are the only artifact that lets you tell when a multi-translation update landed.
Lint and format
There is no ESLint, Prettier, Stylelint, or any other linter configured in this repository. .editorconfig is the only formatting rule. Issue #936 tracks a broader audit of tooling dependencies and lockfile health — adopt your own linter via that PR if your fork wants stricter style enforcement.
6. Performance
The site's performance ceiling is set entirely at build time — there is nothing dynamic to tune. The relevant levers are the minifier configurations and the asset pipeline.
Build-time performance
| Lever | Where | Default | Notes |
|---|---|---|---|
| HTML minification | htmlmin transform in eleventy.config.js |
useShortDoctype, removeComments, collapseWhitespace all true |
Conservative — does not enable removeAttributeQuotes or minifyJS (which would re-mangle inlined scripts). |
| CSS minification | cssmin filter (clean-css 5) |
Default options ({}) |
Templates pipe stylesheet content through this filter; output is CSS-spec-compliant. |
| JS minification | jsmin filter (uglify-es 3) |
Default options | Errors are logged but the original code is returned, so a malformed snippet downgrades to verbose output rather than breaking the build. |
| Markdown options | markdown-it constructor |
breaks: true, linkify: true, typographer: true |
breaks: true adds slightly more <br> markup than necessary, but matches GitHub-Flavored Markdown expectations contributors arrive with. |
Runtime performance
The site delivers:
- A single hand-authored
app.jsof 26 lines (mobile nav toggle + resize handler). No frameworks, no polyfills. - Seven hand-authored CSS files (
defaults,variables,header,navigation,content,footer,prism). - Static images in
src/assets/images/:apple-touch-icon.png,bg.png,favicon-32x32.png,favicon-96x96.png,gradient.svg,og-image.png. All are passthrough-copied to_site/assets/images/without further processing.
There is no service worker, no <link rel=preload>, no critical-CSS extraction, and no font subsetting — none of which the site needs given the hand-authored CSS is small.
Tip
If a fork wants to add performance instrumentation (Lighthouse CI, web-vitals reporting), the right hook is the
htmlmintransform — inject your tag there so it lands on every output page. Adding it as a Nunjucks include indefault.njkworks equally well and is easier to disable via env-flag at build time.
Scaling content
Build time grows linearly with file count. Each translation is roughly 600 lines of Markdown; the entire 34-translation corpus rebuilds in seconds even on a low-spec runner. There is no incremental-build cache configured, but Eleventy's built-in incremental mode (--incremental) can be added to npm start for faster dev rebuilds on very large content sets.
Image handling
Images live in src/assets/images/ and are passthrough-copied. There is no responsive-image plugin, no WebP/AVIF conversion, and no srcset generation. The OG image (og-image.png) and the gradient SVG (gradient.svg) are the only assets referenced from templates; favicons are referenced via <link> in the default.njk layout head.
Note
If a fork adds many images (e.g., diagrams illustrating questions), the
@11ty/eleventy-imgplugin is the canonical pattern in the Eleventy ecosystem — it integrates cleanly with the existing passthrough-copy setup and produces multi-format<picture>markup with build-time conversion.
7. Security
This is a static documentation site whose content is intentionally answer-free interview questions. The threat surface is small. The risks that exist are concentrated in supply-chain and translation-quality, not in code execution.
Threat model summary
| Asset | Threat | Mitigation |
|---|---|---|
| The published site | XSS via untrusted Markdown | Translations and questions are PR-reviewed; markdown-it's html: true flag does allow raw HTML, so reviewers must check translation HTML for <script> injection. |
| The build pipeline | Compromised npm dependency | Lockfile is committed; pins are tilde/caret-controlled. Issue #936 tracks formal supply-chain auditing. |
| Repository | Direct push of malicious content | GitHub branch protection (Issue #934 tracks migrating from legacy branch protection to repo Rulesets). |
| Translation accuracy | Politically or technically misleading translations | All translation PRs are review-gated; translation update issues (#812, #827, #828, #834, #840, #853) act as ongoing audit triggers. |
Caution
The Markdown engine is configured with
html: true. This means a malicious translator could in principle ship a translation file containing inline<script>tags, and those tags would render verbatim into the deployed site. Every translation PR must be reviewed for any HTML content the translator added —<div dir="rtl">,<span lang="...">, anchor tags, and similar are routine;<script>,<iframe>,<embed>,<object>, inline event handlers (onclick,onerror, etc.), andjavascript:URLs are not.
Supply-chain posture
The committed package-lock.json (lockfileVersion 3) pins every transitive dependency. Notable observations:
axiosis force-pinned to0.18.1via theresolutionsblock — a yarn-style override for a known-vulnerable older version some Eleventy 2.x transitives request.uglify-es^3.3.9is no longer maintained upstream (replaced byterser). It still works for the project's small JS surface but is a long-term replacement candidate.html-minifier^4.0.0has been functionally superseded byhtml-minifier-terserin many static-site setups. Either is fine for the current codebase.
The current snapshot (commit 33f5498, dated 2026-03-12) reflects the open audit work in issue #936 — Audit Tooling Dependencies & Package Lock Health.
Warning
Re-running
npm audit fix --forceon this repository can be destructive: forcing major-version bumps oneleventyor its plugins changes the i18n plugin's API, which in turn breaks every translation page. Run audits in a branch, build, and visually verify the language switcher and at least three translation pages before merging. Issue #936 tracks this carefully precisely because the blast radius is the entire translation index.
"No answers" stance
A unique aspect of this project's posture is what is not in the repository: answers. The README and CONTRIBUTING explicitly avoid publishing canonical answers because:
- The questions are designed to surface a candidate's reasoning, not their memory of a textbook answer.
- Publishing a rote answer key would let candidates pass the "test" without demonstrating the underlying skill.
- Several questions are deliberately open-ended — there is no single right answer.
Maintainers reject PRs that add inline answers. If a fork wants an answer-key version, that is the fork's choice — but the canonical h5bp repo will not accept those changes upstream.
Branch protection
Issue #934 tracks migrating from GitHub's legacy branch-protection settings to the newer Rulesets API, which gives finer-grained control over required reviews, status checks, and merge methods. Until that lands, the legacy protection on main is the primary defense against direct unreviewed pushes.
Privacy and data collection
The published site collects no user data. There is no analytics tag in the templates, no fonts loaded from third parties, and no embedded iframes or external scripts. Visitors fetch HTML/CSS/images from the host and nothing else. (If a fork adds analytics, do so in default.njk so the change is reviewable in one place.)
8. Developer Internals
This section is the deep dive into how the build resolves files, what each module does, and the patterns to follow when extending the codebase.
Repository structure
.
+-- .all-contributorsrc all-contributors CLI config
+-- .editorconfig Editor formatting rules
+-- .gitignore Excludes node_modules/, _site/
+-- CONTRIBUTORS.md Generated contributor table
+-- LICENSE.md MIT license
+-- README.md Repo intro, contributor table
+-- package.json Scripts + devDependencies
+-- package-lock.json Pinned dependency tree (lockfileVersion 3)
+-- config/
| +-- eleventy.config.js The build pipeline (the only build config)
+-- src/
+-- index.njk Site root page
+-- about.njk Project history, OG contributors
+-- 404.njk 404 page
+-- sitemap.xml.njk Sitemap generator (uses html_date_string)
+-- translations.njk Language index page
+-- _data/
| +-- site.json title, url, description, twitter
| +-- questions.json 9 internal categories + 1 external (A11y)
| +-- translations.json 34 locale entries
| +-- contributors.json Generated by all-contributors
| +-- maintainers.json Active + legacy maintainer roster
| +-- og.json 14 founding contributors
| +-- helpers.js currentYear() helper
+-- _includes/
| +-- layouts/
| | +-- default.njk Site shell (head, header, footer)
| | +-- page.njk Wraps a content page in default
| +-- components/
| | +-- footer.njk Footer partial
| | +-- navigation.njk Header nav partial
| +-- assets/
| +-- css/
| | +-- defaults.css Global resets
| | +-- variables.css CSS custom properties
| | +-- header.css Header styling
| | +-- navigation.css Nav styling (mobile-aware)
| | +-- content.css Question-list typography
| | +-- footer.css Footer styling
| | +-- prism.css Syntax-highlight theme
| +-- js/
| +-- app.js Mobile nav toggle + resize handler
+-- assets/images/
| +-- apple-touch-icon.png
| +-- bg.png
| +-- favicon-32x32.png
| +-- favicon-96x96.png
| +-- gradient.svg
| +-- og-image.png
+-- questions/
| +-- coding-questions.md
| +-- css-questions.md
| +-- fun-questions.md
| +-- general-questions.md
| +-- html-questions.md
| +-- javascript-questions.md
| +-- network-questions.md
| +-- performance-questions.md
| +-- testing-questions.md
+-- translations/
+-- _template/README.md Skeleton for new locales
+-- arabic/README.md (RTL — uses <div dir="rtl">)
+-- bengali/README.md
+-- bulgarian/README.md
+-- ... (30 more)
+-- vietnamese/README.md
+-- korean/Reference.md (the only translation with a second file)
Note that src/translations/korean/ contains a second file Reference.md — a Korean-language reference document beyond the standard README.md. This is the only translation locale that ships an extra file.
How file discovery works
Eleventy's behavior is governed by the templateFormats array. With ['njk', 'md', 'html'], every file under src/ whose extension matches becomes a page unless its path starts with _ (which marks it as part of the data cascade or the includes tree). That means:
src/index.njkbecomes_site/index.html.src/about.njkbecomes_site/about/index.html(Eleventy's default permalink format).src/questions/javascript-questions.mdbecomes_site/questions/javascript-questions/index.html.src/translations/japanese/README.mdbecomes_site/translations/japanese/index.html, because the Japanese README has front matter settingpermalink: /translations/japanese/index.html.src/_data/*.jsonandsrc/_includes/**/*are NOT pages — they are data and partials respectively.
How locales are resolved
The EleventyI18nPlugin is registered with defaultLanguage: 'en'. At template time, locale-aware helpers (locale_url, getLocaleUrl) become available; templates use them to render the language-switcher links. Each translation file declares its locale via the lang front-matter key (e.g., lang: ja, lang: ar), and the plugin uses that to resolve cross-locale links.
How navigation is generated
The header navigation reads src/_data/questions.json directly. Each entry's abbreviation is rendered as a short link label, title is the tooltip/accessible name, and url is the destination. The external: true flag changes the link's rendering (typically opens in a new tab and gets a visual indicator) — only the A11y row uses this currently.
Module reference (HIGH-priority modules)
The repository is small enough that "module reference" maps to "every JS file":
| Module | Path | Length | Purpose |
|---|---|---|---|
| Build config | config/eleventy.config.js |
~95 lines | The single source of truth for the build pipeline (plugins, filters, transforms, directory layout). |
| Helpers | src/_data/helpers.js |
5 lines | Exposes currentYear() to templates. |
| Client JS | src/_includes/assets/js/app.js |
26 lines | Mobile-menu toggle (click on #navigation-toggle toggles .open on #navigation) and resize handler (debounced via setTimeout, removes .active if viewport widens past 960px, briefly applies .no-motion to suppress layout-shift animation during resize). |
The full source of app.js:
console.log(
'%cA black belt is a white belt that didn\'t quit.',
'color: #fff; font-size: 20px; text-shadow: 1px 1px 1px #000; font-family: serif;'
)
var navToggle = document.getElementById('navigation-toggle')
var nav = document.getElementById('navigation')
navToggle.addEventListener('click', function() {
nav.classList.toggle('open')
})
window.addEventListener('resize', function() {
if (document.width > 960) {
nav.classList.remove('active')
} else {
nav.classList.add('no-motion')
setTimeout(function () {
nav.classList.remove('no-motion')
}, 250);
}
})
Note
document.widthis a non-standard, deprecated property — modern browsers exposedocument.documentElement.clientWidthorwindow.innerWidthinstead. The property still works in legacy modes for back-compat, but a future refactor should replace it. This is a candidate item for the "Project Soul" content audit tracked in #935.
Code patterns and conventions
- No build step for client JS or CSS beyond the Eleventy filters. Contributors edit hand-authored files; minification happens at build time.
- Variable declarations use
var, notlet/const. This is intentional given the broad audience (the site is itself an interview-questions reference and is read by candidates studying older JS idioms). New code is welcome to use modern syntax — there is no enforced linter. - Semicolons are absent in
app.jsandeleventy.config.js. The project uses ASI (automatic semicolon insertion) consistently. If you add code, match the surrounding style. - Translation files mirror the English structure. Every translation
README.mduses the same H2 category headings (or their localized equivalent) and the same H3 sub-section ordering. Anchors are referenced from in-page TOCs, so renaming or reordering breaks navigation. - Markdown-it filters are content-aware. The
github_permalinkandpermalinkfilters specifically handle the path-prefix translation between site URLs and GitHub source URLs — useful when cross-linking from the site to the repo and back.
Testing strategy
There is no automated test suite. The site's correctness is verified by:
npm run build— the build either succeeds or fails. A template error fails the build immediately.- Visual review of the dev server output during
npm start. - PR review by maintainers.
Adding a test layer (for example, a Pa11y or Axe accessibility crawl, or a lychee link checker over _site/) would be a high-value contribution and fits naturally as a GitHub Actions step before the deploy job.
Note
Issue #935 — Project Soul: Content Audit and Archiving Legacy Front-End Concepts — is the canonical place to discuss removing or archiving questions that have aged out (Flash, jQuery-only patterns,
document.write, etc.). Treat that issue as the editorial backlog.
Potential improvements
A short, opinionated list grounded in the current state of the code:
- Replace
varwithconst/letinapp.js. Tiny diff, signals modernity. - Replace
document.widthwithwindow.innerWidth. The deprecated property is the only true latent bug. - Swap
uglify-esforterser.uglify-esis unmaintained;terseris its successor and a drop-in replacement for the way the filter is used. - Adopt a CSS-in-build-step linter (Stylelint) and a JS linter (ESLint) — neither exists today. Issue #936 is the right thread.
- Add
@11ty/eleventy-plugin-rssif the site wants a feed for "newly added question" updates. Not currently needed but trivial to add. - Add a
lycheelink-check job to catch dead external links in translations. The corpus has ~100 outbound links and they age. - Migrate to
eleventy@3when the i18n plugin's v3 API is stable. Current pin is 2.0.1; v3 has a different module-style export and a few breaking renames.
Contributing guide
Contributors should:
- Fork and clone.
npm install.npm start— verify the dev server runs.- Make changes following the patterns above.
- Run
npm run buildto confirm production build succeeds. - Open a PR with a brief description of the change. For translation PRs, include which questions were updated and link the relevant tracking issue (#812, #827, #828, #834, #840, #853, etc.).
- The maintainer team (Darcy Clarke, Rob Larsen, Vasiliy Vanchuk) reviews and merges.
Note
The contributor manifest in
.all-contributorsrcrecognises eleven distinct contribution types:ideas,doc,infra,review,question,talk,maintenance,translation,code,content,design. When adding a contributor vianpm run contributors:add, pick the most accurate set — the bot uses these to render badges on the README contributor table.
9. Integration Points
The repository is content. The interesting integration surface is for tools and forks that want to consume the question corpus — not to embed the site, but to build adjacent products from the structured Markdown.
Linking from a study app
The simplest integration is a deep link from a study application or a candidate-prep platform back to the canonical site. The URL scheme is stable:
https://h5bp.org/Front-end-Developer-Interview-Questions/ <- root
https://h5bp.org/Front-end-Developer-Interview-Questions/questions/<category>/ <- category page
https://h5bp.org/Front-end-Developer-Interview-Questions/translations/<lang>/ <- translation
The nine internal category slugs are: coding-questions, css-questions, fun-questions, general-questions, html-questions, javascript-questions, network-questions, performance-questions, testing-questions. The 34 translation slugs are the lowercase language names listed in src/_data/translations.json.
Scraping the structured Markdown
Because every question lives in plain Markdown under src/questions/*.md, a downstream tool can consume the corpus by:
- Cloning the repo (or fetching the raw files via the GitHub raw-content URL).
- Parsing each file with any Markdown parser. Question items are list elements (
-or*) under H3 sub-section headings, which themselves sit under H2 category headings. - Joining each question to its category via the file name (
<category>-questions.md).
Example raw-content URL pattern:
https://raw.githubusercontent.com/h5bp/Front-end-Developer-Interview-Questions/main/src/questions/javascript-questions.md
The same pattern works for translations:
https://raw.githubusercontent.com/h5bp/Front-end-Developer-Interview-Questions/main/src/translations/japanese/README.md
Citing the project
The MIT license permits commercial and non-commercial reuse with attribution. A typical citation:
Questions adapted from h5bp/Front-end-Developer-Interview-Questions, maintained by H5BP and 100+ community contributors. Licensed MIT.
If you ship a derived product (a study app, a question bank for a hiring platform, a flashcard deck), include the attribution in your About page or licenses screen.
Embedding the site
Embedding the live site via <iframe> is technically possible but framed by the host's X-Frame-Options policy. GitHub Pages does not currently send X-Frame-Options: DENY, so iframes work — but they hand control of the user experience to the embedded site, including any future policy changes. A scrape-and-render approach is generally a better integration model.
Webhooks and the GitHub event surface
There is no first-party webhook surface — the project does not run a server. Downstream tools that want to react to question additions can:
- Subscribe to GitHub's
pushevents for themainbranch via a GitHub App or webhook. - Filter for changes under
src/questions/**orsrc/translations/**. - Re-index their local mirror of the corpus.
GitHub's commits feed (https://github.com/h5bp/Front-end-Developer-Interview-Questions/commits/main.atom) is a zero-auth alternative — a polling consumer can re-fetch on every new entry.
Tooling integrations
- all-contributors: The
.all-contributorsrcfile is consumed by theall-contributorsGitHub App and CLI. Forks can keep using the same workflow without changes. - GitHub Actions: There is no committed
.github/workflows/in the briefing, so deploy automation is configured at the repo level (not in source). Issue #932 is the relevant migration thread. - GitHub Discussions / Issues: The project uses GitHub Issues for both content updates (translation drift, new question proposals) and infrastructure (tooling audits, CI migration). Use the existing labels rather than introducing parallel taxonomies.
The published site as an OSS reference
The project itself is one of the most-cited open-source interview-question banks for front-end roles. Forks that want to publish a customised version typically:
- Fork on GitHub.
- Update
src/_data/site.jsonwith their own title, URL, description, and Twitter handle. - Update the
--pathprefixinpackage.json'sbuildscript to match the fork's deploy path. - Edit
src/questions/*.mdto add company-specific or role-specific questions. - Optionally trim the translation set in
src/_data/translations.jsonto only the languages the fork supports. - Deploy to GitHub Pages, Netlify, or Vercel.
Tip
If you fork and rename, update the page title in
site.json, the anchor text indefault.njk, and the OG image insrc/assets/images/og-image.png. Forgetting any of these three is the most common cosmetic miss in derived repos.