Elevasis LogoElevasis Docs
Training

Content Development Guide

How to create training lessons, quizzes, and courses using the reusable component library

Implementation: packages/ui/src/components/training/

This guide covers how to develop training content using the reusable component library.


Quick Start

// Minimal lesson structure
import { LessonContainer, Callout, KeyTakeaways } from '@repo/ui'
import { Stack, Text } from '@mantine/core'

export default function Lesson01Introduction() {
  return (
    <LessonContainer
      title="Introduction"
      subtitle="Getting started with the platform"
      duration="10 min"
    >
      <Stack>
        <Text>Your lesson content here...</Text>

        <Callout type="info" title="What you'll learn">
          <Text>- First topic</Text>
          <Text>- Second topic</Text>
        </Callout>

        <KeyTakeaways items={[
          'First key point',
          'Second key point'
        ]} />
      </Stack>
    </LessonContainer>
  )
}

Note: Stack uses gap="md" by default from the theme (componentThemes.tsx). Do not override this in lessons.


Component Library

Layout Components

LessonContainer

Wrapper for all lesson content. Provides consistent padding, title, and subtitle.

import { LessonContainer } from '@repo/ui'

<LessonContainer
  title="Lesson Title"
  subtitle="Course Name"
  duration="15 min"
>
  {/* Lesson content */}
</LessonContainer>
PropTypeDescription
titlestringLesson title displayed in header
subtitlestringCourse name or section
durationstringEstimated reading time
childrenReactNodeLesson content

LessonContent

Styled typography wrapper with proper line height for readability.

import { LessonContent } from '@repo/ui'

<LessonContent>
  <h2>Section Title</h2>
  <p>Paragraph content with proper styling...</p>
</LessonContent>

Content Highlight Components

Callout

Highlight boxes for important information. Four types available.

import { Callout } from '@repo/ui'

<Callout type="info" title="Note">
  Information the learner should know.
</Callout>

<Callout type="tip" title="Pro Tip">
  Helpful advice or best practices.
</Callout>

<Callout type="warning" title="Caution">
  Something to be careful about.
</Callout>

<Callout type="danger" title="Important">
  Critical information or common mistakes.
</Callout>
TypeColorUse For
infoBlueGeneral information, context
tipGreenBest practices, helpful hints
warningYellowThings to be careful about
dangerRedCritical warnings, common mistakes

QuickFact

Single-line highlight for interesting facts or statistics.

import { QuickFact } from '@repo/ui'

<QuickFact>
  AI agents can reduce manual work by up to 80% in repetitive tasks.
</QuickFact>

KeyTakeaways

Summary box for lesson conclusions. Place at the end of each lesson.

import { KeyTakeaways } from '@repo/ui'

<KeyTakeaways items={[
  'First key point learners should remember',
  'Second key point',
  'Third key point'
]} />

// Custom title
<KeyTakeaways title="Summary" items={[...]} />

Visual Content Components

SectionHeader

Section divider with optional icon and gradient underline. Has built-in spacing (mt="xl" above, mb="md" below) - do not add additional margins.

import { SectionHeader } from '@repo/ui'
import { IconBrain } from '@tabler/icons-react'

<SectionHeader
  icon={IconBrain}
  title="AI Fundamentals"
  color="blue"
  order={2}
/>
PropTypeDefaultDescription
iconIcon-Tabler icon component
titlestring-Section title
colorstring'blue'Mantine color
order2 | 3 | 42Heading level

FeatureCard

Card for highlighting features or concepts with an icon.

import { FeatureCard } from '@repo/ui'
import { IconBrain } from '@tabler/icons-react'

<FeatureCard
  icon={IconBrain}
  title="AI Agents"
  description="Specialized AI workers that perform specific tasks"
  color="violet"
/>

ConceptGrid

Grid layout for displaying multiple related concepts.

import { ConceptGrid } from '@repo/ui'
import { IconRoute, IconBrain, IconPlug } from '@tabler/icons-react'

<ConceptGrid
  columns={3}
  items={[
    {
      icon: IconRoute,
      title: 'Workflows',
      description: 'Step-by-step processes',
      color: 'blue'
    },
    {
      icon: IconBrain,
      title: 'Agents',
      description: 'AI decision makers',
      color: 'violet'
    },
    {
      icon: IconPlug,
      title: 'Integrations',
      description: 'External connections',
      color: 'cyan'
    }
  ]}
/>

StepCard

Numbered step for procedural content.

import { StepCard } from '@repo/ui'

<StepCard step={1} title="Create a new workflow" isActive>
  <Text>Navigate to the Workflows section...</Text>
</StepCard>

<StepCard step={2} title="Configure triggers">
  <Text>Set up how the workflow starts...</Text>
</StepCard>

ProcessTimeline

Vertical timeline for displaying sequential processes, workflows, or step-by-step procedures.

import { ProcessTimeline } from '@repo/ui'
import { IconBrain, IconPlayerPlay, IconCircleCheck, IconRefresh } from '@tabler/icons-react'

<ProcessTimeline
  steps={[
    { title: 'Think', description: 'Analyze the situation and plan next action', icon: IconBrain },
    { title: 'Act', description: 'Execute using available tools', icon: IconPlayerPlay },
    { title: 'Evaluate', description: 'Check if goal is achieved', icon: IconCircleCheck },
    { title: 'Loop', description: 'Repeat until task is complete', icon: IconRefresh },
  ]}
/>
PropTypeDefaultDescription
stepsProcessStep[]-Array of steps with title, description, and icon
activenumbersteps.length - 1Index of last active step (0-indexed)
bulletSizenumber24Size of bullet icons in pixels

ProcessStep interface:

interface ProcessStep {
  title: string      // Step title
  description: string // Step description
  icon: Icon         // Tabler icon component
}

When to use ProcessTimeline:

  • Sequential workflows (Trigger -> Step 1 -> Step 2 -> Output)
  • Agent loops (Think -> Act -> Evaluate -> Loop)
  • Multi-step processes (Receive -> Research -> Score -> Update)

When NOT to use:

  • Non-sequential lists (use List instead)
  • Category lists (Research, Analysis, Creativity - use bullets)
  • Comparison tables (use ReferenceTable)

Code Components

CodeBlock

Syntax-highlighted code with copy button.

import { CodeBlock } from '@repo/ui'

<CodeBlock language="typescript" title="Example Agent">
  {`const agent = new Agent({
  name: 'My Agent',
  model: 'claude-sonnet-4'
})`}
</CodeBlock>

<CodeBlock language="bash" showLineNumbers>
  {`npm install @repo/ui
npm run dev`}
</CodeBlock>
PropTypeDefaultDescription
languagestring'typescript'Syntax highlighting language
titlestring-Optional header label
showLineNumbersbooleanfalseShow line numbers
childrenstring-Code content

Supported languages: typescript, javascript, bash, json, sql, yaml, python, etc.


Progress Components

ProgressRing

Circular progress indicator.

import { ProgressRing } from '@repo/ui'

<ProgressRing value={75} label="Course Progress" />

<ProgressRing value={100} label="Complete!" size={80} thickness={8} />

LessonNavigation

Sticky bottom bar with prev/next and progress. Used automatically by the lesson route - you don't need to include this in lesson content.


Quiz Components

LessonQuiz

Modal-based quiz with one question at a time.

// Quiz component file (Lesson01Quiz.tsx)
import { LessonQuiz, type QuizQuestion } from '@repo/ui'

const questions: QuizQuestion[] = [
  {
    id: 'q1',
    question: 'What is the primary purpose of workflows?',
    options: [
      { value: 'a', label: 'AI decision making' },
      { value: 'b', label: 'Step-by-step automation' },
      { value: 'c', label: 'Data storage' }
    ],
    correctAnswer: 'b'
  },
  {
    id: 'q2',
    question: 'When should you use an agent instead of a workflow?',
    options: [
      { value: 'a', label: 'For predictable processes' },
      { value: 'b', label: 'For tasks requiring judgment' },
      { value: 'c', label: 'For scheduled tasks' }
    ],
    correctAnswer: 'b'
  }
]

// Export config for course.config.ts
export const lesson01QuizConfig = { questions }

export default function Lesson01Quiz({ onComplete, onContinue }) {
  return (
    <LessonQuiz
      questions={questions}
      onComplete={(answers, score) => onComplete?.(answers, score)}
      onContinue={onContinue}
    />
  )
}

Quiz Features:

  • Questions and options are shuffled each attempt
  • 100% passing score required
  • Shows correct answers after submission
  • Retry available if failed

AssessmentQuiz

Full-page assessment for course certifications. Supports multiple question types and keyboard navigation.

// FinalAssessment.tsx
import { AssessmentQuiz, type AssessmentQuestion, type AssessmentResult } from '@repo/ui'

export const finalAssessmentConfig = {
  questions: [
    // Radio question (single select)
    {
      id: 'q1',
      type: 'radio',
      question: 'What is the primary purpose of workflows?',
      description: 'Think about predictability and consistency.',
      options: [
        { value: 'a', label: 'AI decision making', description: 'Requires judgment' },
        { value: 'b', label: 'Step-by-step automation', description: 'Predictable processes' },
        { value: 'c', label: 'Data storage', description: 'Persistence layer' }
      ],
      correctAnswer: 'b'
    },
    // Checkbox question (multi-select)
    {
      id: 'q2',
      type: 'checkbox',
      question: 'Which are valid trigger types? (Select all that apply)',
      options: [
        { value: 'manual', label: 'Manual (button click)' },
        { value: 'scheduled', label: 'Scheduled (time-based)' },
        { value: 'event', label: 'Event-driven (webhook)' },
        { value: 'invalid', label: 'Telepathic (mind reading)' }
      ],
      correctAnswers: ['manual', 'scheduled', 'event']
    }
  ] as const satisfies readonly AssessmentQuestion[]
}

interface FinalAssessmentProps {
  onComplete?: (answers: Record<string, unknown>, score: number) => void
}

export default function FinalAssessment({ onComplete }: FinalAssessmentProps) {
  const handleComplete = (result: AssessmentResult) => {
    onComplete?.(result.answers, result.score)
  }

  return (
    <AssessmentQuiz
      questions={finalAssessmentConfig.questions}
      passingScore={80}
      onComplete={handleComplete}
      shuffle={true}
    />
  )
}
PropTypeDefaultDescription
questionsAssessmentQuestion[]-Array of radio or checkbox questions
passingScorenumber80Minimum score to pass (0-100)
onComplete(result) => void-Called when assessment submitted
onRetry() => void-Called when user retries after failing
shufflebooleantrueShuffle questions and options

Question Types:

TypeSelectionAnswer Field
radioSingle optioncorrectAnswer: string
checkboxMultiple optionscorrectAnswers: string[]

AssessmentQuiz vs LessonQuiz:

FeatureLessonQuizAssessmentQuiz
DisplayModal overlayFull page
Question TypesRadio onlyRadio + Checkbox
Option DescriptionsNoYes
Keyboard NavigationNoYes (A-D, Enter)
Default Pass Score100%80%
Use CaseEnd-of-lesson checksCourse certification

Training-Specific Components

Located in @/features/training/components - these enforce brand consistency.

SectionHeader

Section header with icon and gradient underline. Default color is blue. Inherits built-in spacing (mt="xl" above, mb="md" below).

import { SectionHeader } from '@repo/ui'
import { IconBrain } from '@tabler/icons-react'

<SectionHeader icon={IconBrain} title="AI Fundamentals" />

TrainingConceptGrid

ConceptGrid with training brand color enforced.

import { TrainingConceptGrid } from '@/features/training/components'
import { IconRoute, IconBrain } from '@tabler/icons-react'

<TrainingConceptGrid
  columns={2}
  items={[
    { icon: IconRoute, title: 'Workflows', description: 'Step sequences' },
    { icon: IconBrain, title: 'Agents', description: 'AI decision makers' }
  ]}
/>

ReferenceTable

Tabular data display with glass styling.

import { ReferenceTable } from '@/features/training/components'
import { IconRoute, IconBrain } from '@tabler/icons-react'

<ReferenceTable
  columns={[
    { header: 'Component', key: 'name', width: '25%' },
    { header: 'Purpose', key: 'purpose' },
    { header: 'Use When', key: 'when' }
  ]}
  rows={[
    {
      id: 'workflow',
      name: 'Workflow',
      purpose: 'Execute predictable steps',
      when: 'Process is repeatable',
      icon: IconRoute,
      color: 'blue'
    },
    {
      id: 'agent',
      name: 'Agent',
      purpose: 'Make decisions',
      when: 'Task requires judgment',
      icon: IconBrain,
      color: 'violet'
    }
  ]}
/>

Lesson Structure Pattern

Follow this structure for consistent lessons:

import {
  LessonContainer,
  Callout,
  KeyTakeaways,
  SectionHeader
} from '@repo/ui'
import { Text, Stack } from '@mantine/core'
import { TrainingConceptGrid } from '@/features/training/components'
import { IconTopic } from '@tabler/icons-react'

export default function Lesson01TopicName() {
  return (
    <LessonContainer
      title="Topic Name"
      subtitle="Description of what this covers"
      duration="10 min"
    >
      <Stack>
        {/* 1. Introduction */}
        <Text size="lg">
          Opening paragraph that sets context and hooks the learner.
        </Text>

        {/* 2. Learning objectives */}
        <Callout type="info" title="What you'll learn">
          <Stack gap="xs">
            <Text>- First learning objective</Text>
            <Text>- Second learning objective</Text>
            <Text>- Third learning objective</Text>
          </Stack>
        </Callout>

        {/* 3. Main content sections */}
        <SectionHeader icon={IconTopic} title="Section One" />
        <Text>Content for section one...</Text>

        <SectionHeader icon={IconTopic} title="Section Two" />
        <Text>Content for section two...</Text>

        {/* 4. Key takeaways */}
        <KeyTakeaways
          items={[
            'First key point',
            'Second key point',
            'Third key point'
          ]}
        />

        {/* 5. Transition to next lesson */}
        <Callout type="info" title="Next Up">
          In the next lesson, we'll explore...
        </Callout>
      </Stack>
    </LessonContainer>
  )
}

Spacing Standards:

  • Stack uses theme default gap="md" - do not override
  • SectionHeader has built-in margins (mt="xl", mb="md") - do not add extra spacing
  • List components don't need mt props - Stack gap handles spacing
  • Only use gap="xs" inside Callouts for tightly grouped items

Quiz File Pattern

Each lesson quiz is a separate file:

// Lesson01TopicQuiz.tsx
import { LessonQuiz, type QuizQuestion } from '@repo/ui'

const questions: QuizQuestion[] = [
  {
    id: 'q1',
    question: 'Question text here?',
    options: [
      { value: 'a', label: 'Option A' },
      { value: 'b', label: 'Option B' },
      { value: 'c', label: 'Option C' },
      { value: 'd', label: 'Option D' }
    ],
    correctAnswer: 'b'
  }
  // Add 3-5 questions per quiz
]

// Export for course.config.ts
export const lesson01TopicQuizConfig = { questions }

interface Props {
  onComplete?: (answers: Record<string, unknown>, score: number) => void
  onContinue?: () => void
}

export default function Lesson01TopicQuiz({ onComplete, onContinue }: Props) {
  return (
    <LessonQuiz
      questions={questions}
      onComplete={(answers, score) => onComplete?.(answers, score)}
      onContinue={onContinue}
    />
  )
}

Course Configuration

Register lessons in course.config.ts:

import type { CourseConfig } from '../../types'

// Lesson imports
import Lesson01Topic from './Lesson01Topic'
import Lesson02Topic from './Lesson02Topic'

// Quiz imports
import Lesson01TopicQuiz, { lesson01TopicQuizConfig } from './Lesson01TopicQuiz'
import Lesson02TopicQuiz, { lesson02TopicQuizConfig } from './Lesson02TopicQuiz'

// Final assessment (if certified)
import FinalAssessment, { finalAssessmentConfig } from './FinalAssessment'

export const myCourse: CourseConfig = {
  slug: 'my-course',
  title: 'My Course',
  description: 'Course description for catalog.',
  audience: 'customer',  // 'internal' | 'developer' | 'customer' | 'public'
  estimatedDuration: '60 min',
  lessons: [
    {
      slug: 'topic-one',
      title: 'Topic One',
      description: 'What this lesson covers.',
      duration: '10 min',
      order: 1,
      component: Lesson01Topic,
      quiz: {
        passingScore: 100,
        questions: lesson01TopicQuizConfig.questions,
        component: Lesson01TopicQuiz
      }
    },
    {
      slug: 'topic-two',
      title: 'Topic Two',
      description: 'What this lesson covers.',
      duration: '15 min',
      order: 2,
      component: Lesson02Topic,
      quiz: {
        passingScore: 100,
        questions: lesson02TopicQuizConfig.questions,
        component: Lesson02TopicQuiz
      }
    }
  ],
  assessments: [
    {
      slug: 'final-assessment',
      title: 'Course Certification',
      passingScore: 80,
      questions: finalAssessmentConfig.questions,
      component: FinalAssessment
    }
  ],
  certification: {
    certificationSlug: 'my-course-certified',
    requiredLessons: 'all',
    requiredAssessments: [{ slug: 'final-assessment', minScore: 80 }]
  }
}

Theme Colors

Use the centralized theme for consistency:

import {
  TRAINING_PRIMARY_COLOR,
  TRAINING_SEMANTIC_COLORS,
  getTrainingColor
} from '@/features/training/components'

// Primary color (blue) - use for most content
TRAINING_PRIMARY_COLOR // 'blue'

// Semantic colors - use sparingly for specific meanings
TRAINING_SEMANTIC_COLORS.success  // 'green' - positive outcomes
TRAINING_SEMANTIC_COLORS.warning  // 'orange' - caution
TRAINING_SEMANTIC_COLORS.error    // 'red' - negative outcomes
TRAINING_SEMANTIC_COLORS.neutral  // 'gray' - dimmed content

// Helper function
getTrainingColor('success') // 'green'
getTrainingColor()          // 'blue' (default)

Best Practices

Spacing Guidelines

  1. Use theme defaults - Stack uses gap="md" from theme - do not override
  2. No inline margins on Lists - Stack gap handles spacing between elements
  3. SectionHeader has built-in spacing - mt="xl" above, mb="md" below
  4. Use gap="xs" inside Callouts - For tightly grouped items within callouts

Content Guidelines

  1. Keep lessons focused - One concept per lesson, 8-15 minutes reading time
  2. Start with context - Opening paragraph explains why this matters
  3. Use visuals - ConceptGrid, FeatureCard, and diagrams break up text
  4. End with takeaways - KeyTakeaways summarizes key points
  5. Preview next lesson - Callout at end creates continuity

Quiz Guidelines

  1. 3-5 questions per lesson - Tests understanding, not memorization
  2. Clear correct answers - Avoid trick questions or ambiguous options
  3. Test key concepts - Questions should cover KeyTakeaways content

Passing Score Standards:

  • Lesson quizzes: 100% - Users must answer all questions correctly to proceed
  • Final assessments: 80% - Required for certification

Accessibility

  1. Alt text for images - Describe what the image shows
  2. Semantic headings - Use proper heading hierarchy (h2, h3, h4)
  3. Color not sole indicator - Don't rely only on color to convey meaning
  4. Readable font sizes - LessonContent provides proper sizing

File Structure

apps/command-center/src/features/training/content/courses/{slug}/
├── course.config.ts           # Course metadata and lesson registry
├── Lesson01TopicName.tsx      # Lesson content component
├── Lesson01TopicNameQuiz.tsx  # Inline quiz for lesson
├── Lesson02TopicName.tsx
├── Lesson02TopicNameQuiz.tsx
├── FinalAssessment.tsx        # Final certification assessment (optional)
└── PlaceholderLesson.tsx      # Placeholder for courses in development


Last Updated: 2026-01-12