diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index d77c6385..3a057057 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -119,6 +119,7 @@ export default defineConfig({
items: [
// { text: 'Main Study Loop', link: '/study-loop' },
{ text: 'System Overview', link: '/learn/architecture' },
+ { text: 'Authoring Cards', link: '/learn/cards' },
{ text: 'Data Layer', link: '/learn/data-layer' },
{ text: 'Application Layer', link: '/learn/apps' },
{ text: 'Pedagogy Defaults', link: '/learn/pedagogy' },
diff --git a/docs/.vitepress/theme/components/EmbeddedFillInEditor.vue b/docs/.vitepress/theme/components/EmbeddedFillInEditor.vue
new file mode 100644
index 00000000..04f64032
--- /dev/null
+++ b/docs/.vitepress/theme/components/EmbeddedFillInEditor.vue
@@ -0,0 +1,308 @@
+
+
+
+
+
+
+
+
+
+
+
Enter markdown above to see the live preview
+
+
Last Response:
+
{{ JSON.stringify(lastResponse, null, 2) }}
+
+
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
index 6d45f0e6..1b89e1e7 100644
--- a/docs/.vitepress/theme/index.ts
+++ b/docs/.vitepress/theme/index.ts
@@ -27,6 +27,7 @@ import '@vue-skuilder/common-ui/style';
// Import components to register globally
import EmbeddedCourse from './components/EmbeddedCourse.vue';
import HeroStudySession from './components/HeroStudySession.vue';
+import EmbeddedFillInEditor from './components/EmbeddedFillInEditor.vue';
export default {
extends: DefaultTheme,
@@ -123,5 +124,6 @@ export default {
// Register global components
app.component('EmbeddedCourse', EmbeddedCourse);
app.component('HeroStudySession', HeroStudySession);
+ app.component('EmbeddedFillInEditor', EmbeddedFillInEditor);
},
} satisfies Theme;
diff --git a/docs/assets/img-card.png b/docs/assets/img-card.png
new file mode 100644
index 00000000..124b0ec6
Binary files /dev/null and b/docs/assets/img-card.png differ
diff --git a/docs/cards.md b/docs/cards.md
deleted file mode 100644
index 4cb981f1..00000000
--- a/docs/cards.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Cards
-
-Test page works.
-
-
-
-## FallingLetters Component (Testing 1)
-
-
diff --git a/docs/dbg/embedded-fill-in-editor-test.md b/docs/dbg/embedded-fill-in-editor-test.md
new file mode 100644
index 00000000..5e43931b
--- /dev/null
+++ b/docs/dbg/embedded-fill-in-editor-test.md
@@ -0,0 +1,78 @@
+# EmbeddedFillInEditor Component Test
+
+Test page for EmbeddedFillInEditor component development and debugging.
+
+
+
+## Basic Fill-In Example
+
+Simple fill-in-the-blank with a single correct answer:
+
+
+
+## Multiple Alternatives Example
+
+Fill-in with multiple acceptable answers:
+
+
+
+## Multiple Choice Example
+
+Multiple choice with correct answer and distractors:
+
+
+
+## Programming Example
+
+Code-related fill-in question:
+
+
+
+## Complex Example with Context
+
+Fill-in with markdown context and code:
+
+
+
+## Progressive Difficulty Examples
+
+### Easy Version
+
+
+### Medium Version
+
+
+### Hard Version
+
+
+## Syntax Testing Area
+
+Use this editor to test different mustache syntax patterns:
+
+
+
+---
+
+## Syntax Reference
+
+The editor supports these mustache patterns:
+
+- **Simple fill-in**: `{{answer}}`
+- **Multiple alternatives**: `{{answer1|answer2}}`
+- **Multiple choice**: `{{correct||distractor1|distractor2}}`
+- **Progressive difficulty**: Add more distractors to increase difficulty
+
+Try editing the examples above to see how different syntax patterns render!
\ No newline at end of file
diff --git a/docs/dbg/index.md b/docs/dbg/index.md
index a10dbb28..90bd2009 100644
--- a/docs/dbg/index.md
+++ b/docs/dbg/index.md
@@ -4,3 +4,5 @@
[local embedded-course-test](./embedded-course-test)
[remote embedded-course-test](./remote-crs-embedding)
+
+[embedded fill-in editor test](./embedded-fill-in-editor-test)
diff --git a/docs/do/theming.md b/docs/do/theming.md
index 258cd572..6ed71d9d 100644
--- a/docs/do/theming.md
+++ b/docs/do/theming.md
@@ -1 +1,46 @@
-todo
+# Themes
+
+Skuilder uses Vuetify 3, and its [theming system](https://vuetifyjs.com/en/features/theme/) can be used to customize colors, typography, and more.
+
+The basic structure is:
+
+```json
+{
+ "theme": {
+ "name": "my-custom-theme",
+ "defaultMode": "light",
+ "light": {
+ "dark": false,
+ "colors": {
+ "primary": "#1976D2",
+ "secondary": "#424242",
+ "accent": "#82B1FF",
+ "error": "#F44336",
+ "warning": "#FF9800",
+ "info": "#2196F3",
+ "success": "#4CAF50"
+ }
+ },
+ "dark": {
+ "dark": true,
+ "colors": {
+ "primary": "#2196F3",
+ "secondary": "#90A4AE",
+ "accent": "#82B1FF",
+ "error": "#EF5350",
+ "warning": "#FFA726",
+ "info": "#42A5F5",
+ "success": "#66BB6A"
+ }
+ }
+ }
+}
+```
+
+## Configuration Location
+
+In a scaffolded course, the theme is defined in your `skuilder.config.json` file at the root of your course directory (the same location where you have your `package.json`).
+
+## User Theme Switching
+
+Users can toggle between modes in the settings menu, and their preference will be remembered across sessions.
diff --git a/docs/learn/cards.md b/docs/learn/cards.md
new file mode 100644
index 00000000..e94fe625
--- /dev/null
+++ b/docs/learn/cards.md
@@ -0,0 +1,147 @@
+
+
+Skuilder's default card type uses **markdown** enhanced with a **moustache** syntax to define its interactivity. The features are described by example in the editors below. The editors are live - play with the contents.
+
+**Note** that the rendering context here - outside of the assumed environment of a skuilder StudySession, with many cards rendered at once - may result in some slightly wonky behaviour.
+
+## Information Cards
+
+When no moustache syntax exists, the card is interpreted as a straightforward markdown document.
+
+
+
+Dismissals of information cards are automatically stamped with `isCorrect: true`.
+
+## Basic Fill-In Example
+
+Simple fill-in-the-blank with a single correct answer. This renders a `TextInput` for the user to enter the answer. In normal context, it will autofocus the input element.
+
+
+
+## Multiple Alternatives Example
+
+Let's be little more forgiving. The below will accept either Paris or paris. Generally, moustache items separated by a `|` are siblings that play the same role in the rendered card.
+
+
+
+
+
+## Fill-In with Multiple Choice
+
+Instead of a user-typed answer, we can render multiple choice
+
+A __blank__ can present as a multiple choice question with correct answer and distractors:
+
+
+
+Here, the double-bar `||` separates the correct answer from distractors, and the single bar `|` separates distractors.
+
+The order of rendered options is randomized, and a maximum of 6 options will be rendered, no matter the number of distractors supplied.
+
+If an item exists as both an `answer` and a `distractor`, it will be interprted only as an answer.
+
+Multiple correct answers can be supplied as well. Exactly one correct answer will be rendered as an option.
+
+
+
+## Multiple Choice as Answer-to-a-Question
+
+If the multiple-choice moustache is the **final** content of the card, it will render the options but insert no blank into the text.
+
+
+
+## On Whitespace
+
+Whitespace inside of moustache notation is trimmed, which can help to write readible card source. The following texts all render the same card:
+
+Dense:
+
+{{whitespaceExamples[0]}}
+
+
+Spaced:
+
+{{whitespaceExamples[1]}}
+
+
+Roomy:
+
+{{whitespaceExamples[2]}}
+
+
+
+## Markdown in Answers
+
+Rendered multiple-choice buttons may also include markdown.
+
+
+
+Including, eg, code blocks:
+
+
+
+## Media in Cards
+
+Basic cards support media (audio, image) that has been bundled with the card. Porting that process to this docs site is a work in progress, but the rough picture is this:
+
+
+
+## General Content Embedding
+
+If it's markdown, it mostly just works. For example, this youtube embed.
+
+Note: if you're reading this and haven't watched this talk, treat yourself.
+
+