A Quarto extension that allows you to password-protect content in HTML documents, perfect for progressively revealing solutions during lectures or workshops. The encryption is not secure and hence suitable for educational use only, not sensitive data.
quarto add pcerf/quarto-password-contentThis will install the extension under the _extensions subdirectory.
If you're using version control, you will want to check in this directory.
Add the filter to your document's YAML header:
---
title: "My Lecture"
include-solutions: false # Set to true to show passwords
filters:
- password-content
---Wrap content you want to protect in a div with the content-password class and a unique name attribute:
## Exercise 1: Build a Pipeline
Try to implement this yourself first...
:::{.content-password name="pipeline-exercise"}
## Solution
```{python}
from sklearn.pipeline import Pipeline
# Your solution code here
```
This solution demonstrates...
:::Important: The name attribute:
- Determines the password (same name = same password)
- Keeps password stable even if you modify the solution content
- Should be unique and descriptive (e.g., "exercise-1", "sklearn-pipeline")
- If omitted, defaults to "solution-1", "solution-2", etc.
When include-solutions: false (student view):
- Content is encrypted and hidden
- A password input field is displayed
- Students must enter the correct 5-character password to reveal
- Password is automatically generated from the solution's
nameattribute
When include-solutions: true (instructor view):
- Content is visible
- A collapsible blue info box shows the solution name and password
- Click to reveal the password
- Share passwords with students during class to unlock solutions
- Encryption: Passwords not stored in HTML source (only hash); content encrypted with XOR cipher
- Security: Suitable for educational use only, not sensitive data
- Stable passwords: Based on solution name, not content (won't change when you edit)
- Deterministic: Same name always generates same password
- Session persistence: Once unlocked, stays unlocked during browser session
- Clean UI: Professional password entry interface with collapsible password boxes
- Keyboard support: Press Enter to submit password
-
Before class: Render with
include-solutions: falsequarto render lecture.qmd
-
During class: Open instructor version with
include-solutions: truequarto render lecture.qmd -M include-solutions:true
-
Share passwords: As you progress, share passwords verbally or via chat
-
Students unlock: Students enter passwords to reveal solutions at their own pace
Here is the source code for a minimal example: example.qmd.
See it in action:
- Student view - Solutions are password-protected
- Instructor view - Shows passwords for each solution
The password-protected content extension is particularly useful for:
- Educational settings: Progressively reveal solutions during lectures
- Workshops: Allow participants to work at their own pace
- Self-paced learning: Provide hints and solutions that learners can unlock
- Interactive tutorials: Hide answers until students attempt exercises
- Password generation: Hash of solution name (not content)
- Password verification: One-way hash stored in HTML (password not recoverable)
- Encryption: XOR cipher with password as key
- Storage: Encrypted content embedded in HTML as hex string
- Decryption: Client-side JavaScript when correct password entered
- Password format: 5 characters (uppercase letters and numbers, excluding similar-looking characters)
- Each solution gets a unique password based on its
nameattribute - Passwords remain stable even if you modify the solution content
- Use descriptive names like "exercise-1", "pipeline-solution", "data-cleaning"
- Keep an instructor copy rendered with
include-solutions: truefor reference - Passwords are 5 characters (uppercase letters and numbers, excluding similar-looking characters)
- The
content-passworddiv only works with HTML output formats - Content is encrypted client-side for security
- Once unlocked, content remains visible during the browser session
- Passwords are deterministically generated from the solution name