- Implement safe markdown processing with marked and DOMPurify - Add markdown-slide layout template with dedicated markdown slots - Support auto-detection of markdown content in text slots - Include comprehensive markdown styling (lists, headers, code, quotes, tables) - Maintain security with HTML sanitization and safe element filtering - Add markdown documentation to theme creation guidelines 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
429 lines
9.8 KiB
Markdown
429 lines
9.8 KiB
Markdown
# Theme Creation Guidelines
|
|
|
|
## Theme Structure
|
|
|
|
Each theme must follow this directory structure:
|
|
```
|
|
themes/
|
|
theme-name/
|
|
style.css # Theme styles and metadata
|
|
master-slide.html # Master slide template (optional)
|
|
layouts/ # Layout templates directory
|
|
layout1.html
|
|
layout2.html
|
|
...
|
|
```
|
|
|
|
## CSS Theme Metadata
|
|
|
|
Theme metadata MUST be included as comments at the top of `style.css`:
|
|
|
|
```css
|
|
/*
|
|
* Theme: [theme-id]
|
|
* Name: [Display Name]
|
|
* Description: [Theme description]
|
|
* Author: [Author Name] ([email])
|
|
* Version: [version]
|
|
*/
|
|
```
|
|
|
|
## CSS Variables System
|
|
|
|
### Required Theme Variables
|
|
Define these CSS custom properties in `:root` for consistent theming:
|
|
|
|
```css
|
|
:root {
|
|
/* Colors */
|
|
--theme-primary: #color; /* Primary brand color */
|
|
--theme-secondary: #color; /* Secondary accent color */
|
|
--theme-accent: #color; /* Interactive accent color */
|
|
--theme-background: #color; /* Slide background */
|
|
--theme-text: #color; /* Primary text color */
|
|
--theme-text-secondary: #color; /* Secondary text color */
|
|
|
|
/* Typography */
|
|
--theme-font-heading: 'Font', fallback;
|
|
--theme-font-body: 'Font', fallback;
|
|
--theme-font-code: 'Font', fallback;
|
|
|
|
/* Layout */
|
|
--slide-padding: 5%; /* Slide edge padding */
|
|
--content-max-width: 90%; /* Max content width */
|
|
}
|
|
```
|
|
|
|
### Required Base Slide Styling
|
|
Always include this base styling that works with the global `.slide-container` classes:
|
|
|
|
```css
|
|
.slide-container .slide-content,
|
|
.slide {
|
|
width: 100%;
|
|
height: 100%;
|
|
background: var(--theme-background);
|
|
color: var(--theme-text);
|
|
font-family: var(--theme-font-body);
|
|
padding: var(--slide-padding);
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
overflow: hidden;
|
|
justify-content: center;
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
```
|
|
|
|
## Layout HTML Templates
|
|
|
|
### Required Layout Structure
|
|
Each layout HTML file must:
|
|
|
|
1. **Use semantic class naming**: `.layout-[layout-name]`
|
|
2. **Include slot elements** for editable content
|
|
3. **Use Handlebars syntax** for template variables
|
|
|
|
### Slot System
|
|
Slots are editable areas defined with specific data attributes:
|
|
|
|
```html
|
|
<div class="slot [slot-type]"
|
|
data-slot="[slot-id]"
|
|
data-placeholder="[placeholder-text]"
|
|
data-required
|
|
data-multiline="true"
|
|
data-accept="image/*"
|
|
data-hidden="true">
|
|
{{slot-id}}
|
|
</div>
|
|
```
|
|
|
|
#### Slot Data Attributes:
|
|
- `data-slot="[id]"`: Unique identifier for the slot
|
|
- `data-placeholder="[text]"`: Placeholder text when empty
|
|
- `data-required`: Mark slot as required (optional)
|
|
- `data-multiline="true"`: Allow multiline text input (optional)
|
|
- `data-accept="image/*"`: For image slots (optional)
|
|
- `data-hidden="true"`: Hide from direct editing (optional)
|
|
|
|
#### Common Slot Types:
|
|
- **Title slots**: `data-slot="title"`
|
|
- **Text content**: `data-slot="content"`
|
|
- **Images**: `data-slot="image"` with `data-accept="image/*"`
|
|
- **Subtitles**: `data-slot="subtitle"`
|
|
- **Markdown content**: `data-slot="content" data-type="markdown"`
|
|
|
|
### Layout CSS Naming Convention
|
|
Style layouts using the pattern: `.layout-[layout-name]`
|
|
|
|
```css
|
|
.layout-my-layout,
|
|
.slide-container .layout-my-layout {
|
|
/* Layout-specific styles */
|
|
}
|
|
|
|
.layout-my-layout .slot[data-slot="title"] {
|
|
/* Slot-specific styles */
|
|
}
|
|
```
|
|
|
|
## Creating New Layouts
|
|
|
|
### 1. Create Layout HTML Template
|
|
Create `themes/[theme]/layouts/my-layout.html`:
|
|
|
|
```html
|
|
<div class="slide layout-my-layout">
|
|
<h1 class="slot title-slot"
|
|
data-slot="title"
|
|
data-placeholder="Slide Title"
|
|
data-required>
|
|
{{title}}
|
|
</h1>
|
|
|
|
<div class="slot content-area"
|
|
data-slot="content"
|
|
data-placeholder="Your content here..."
|
|
data-multiline="true">
|
|
{{content}}
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### 2. Add Layout Styles to theme CSS
|
|
Add to the theme's `style.css`:
|
|
|
|
```css
|
|
/* My Layout */
|
|
.layout-my-layout,
|
|
.slide-container .layout-my-layout {
|
|
justify-content: flex-start;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.layout-my-layout .slot[data-slot="title"] {
|
|
font-size: clamp(1.5rem, 6vw, 2.5rem);
|
|
margin-bottom: 2rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.layout-my-layout .slot[data-slot="content"] {
|
|
flex: 1;
|
|
font-size: clamp(1rem, 2.5vw, 1.25rem);
|
|
text-align: left;
|
|
}
|
|
```
|
|
|
|
## Standard Slot Styles
|
|
|
|
### Required Slot Base Styles
|
|
```css
|
|
.slot {
|
|
position: relative;
|
|
border: 2px dashed transparent;
|
|
min-height: 2rem;
|
|
transition: border-color 0.2s ease;
|
|
width: 100%;
|
|
max-width: var(--content-max-width);
|
|
margin: 0 auto;
|
|
text-align: inherit;
|
|
}
|
|
|
|
.slot:hover,
|
|
.slot.editing {
|
|
border-color: var(--theme-accent);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.slot.empty {
|
|
border-color: var(--theme-secondary);
|
|
opacity: 0.5;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.slot.empty::before {
|
|
content: attr(data-placeholder);
|
|
color: var(--theme-text-secondary);
|
|
font-style: italic;
|
|
}
|
|
```
|
|
|
|
### Slot Type Styles
|
|
```css
|
|
/* Text slots */
|
|
.slot[data-type="title"] {
|
|
font-family: var(--theme-font-heading);
|
|
font-weight: bold;
|
|
line-height: 1.2;
|
|
text-align: center;
|
|
}
|
|
|
|
.slot[data-type="subtitle"] {
|
|
font-family: var(--theme-font-heading);
|
|
line-height: 1.4;
|
|
opacity: 0.8;
|
|
text-align: center;
|
|
}
|
|
|
|
.slot[data-type="text"] {
|
|
line-height: 1.6;
|
|
text-align: left;
|
|
}
|
|
|
|
/* Image slots */
|
|
.slot[data-type="image"] {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #f8fafc;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.slot[data-type="image"] img {
|
|
max-width: 100%;
|
|
max-height: 100%;
|
|
object-fit: contain;
|
|
border-radius: 4px;
|
|
}
|
|
```
|
|
|
|
## Master Slide Templates
|
|
|
|
Master slides provide unchangeable content that appears on all slides. Create `master-slide.html`:
|
|
|
|
```html
|
|
<div class="master-slide footer">
|
|
{{footerText}}
|
|
</div>
|
|
```
|
|
|
|
Style master slide elements:
|
|
```css
|
|
.master-slide {
|
|
position: absolute;
|
|
z-index: 1;
|
|
}
|
|
|
|
.master-slide.footer {
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
padding: 1rem;
|
|
text-align: center;
|
|
font-size: 0.875rem;
|
|
opacity: 0.6;
|
|
}
|
|
```
|
|
|
|
## Responsive Design
|
|
|
|
### Aspect Ratio Support
|
|
Include aspect ratio specific adjustments:
|
|
|
|
```css
|
|
.slide-container.aspect-16-9 .slide-content,
|
|
.slide-container.aspect-16-9 .slide {
|
|
--slide-padding: 4%;
|
|
--content-max-width: 85%;
|
|
}
|
|
|
|
.slide-container.aspect-4-3 .slide-content,
|
|
.slide-container.aspect-4-3 .slide {
|
|
--slide-padding: 6%;
|
|
--content-max-width: 80%;
|
|
}
|
|
```
|
|
|
|
### Mobile Responsive
|
|
Add mobile breakpoints:
|
|
|
|
```css
|
|
@media (max-width: 768px) {
|
|
:root {
|
|
--slide-padding: 3%;
|
|
--content-max-width: 95%;
|
|
}
|
|
|
|
.layout-my-layout .slot[data-slot="title"] {
|
|
font-size: clamp(1.2rem, 5vw, 2rem);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Print Support
|
|
Include print styles for presentation export:
|
|
|
|
```css
|
|
@media print {
|
|
.slide {
|
|
page-break-after: always;
|
|
width: 100%;
|
|
height: 100vh;
|
|
}
|
|
|
|
.slot {
|
|
border: none !important;
|
|
}
|
|
|
|
.slot.empty::before {
|
|
display: none;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Markdown Support
|
|
|
|
### Markdown Slots
|
|
Themes can include slots that automatically process markdown content. This enables rich text formatting while maintaining security.
|
|
|
|
#### Creating Markdown Slots
|
|
```html
|
|
<div class="slot markdown-content"
|
|
data-slot="content"
|
|
data-type="markdown"
|
|
data-placeholder="Enter markdown content..."
|
|
data-multiline="true">
|
|
{{content}}
|
|
</div>
|
|
```
|
|
|
|
#### Supported Markdown Features
|
|
- **Headers**: `# H1`, `## H2`, `### H3`
|
|
- **Text formatting**: `**bold**`, `*italic*`, `~~strikethrough~~`
|
|
- **Lists**: Unordered (`-`, `*`) and ordered (`1.`)
|
|
- **Links**: `[text](url)`
|
|
- **Inline code**: `` `code` ``
|
|
- **Code blocks**: ````markdown```language````
|
|
- **Blockquotes**: `> quote`
|
|
- **Tables**: GitHub-flavored markdown tables
|
|
|
|
#### Security Features
|
|
- **DOMPurify sanitization**: All HTML is sanitized to prevent XSS
|
|
- **No script execution**: JavaScript and dangerous attributes are stripped
|
|
- **Safe HTML subset**: Only presentation-safe HTML elements allowed
|
|
- **URL filtering**: Only safe URL schemes (http, https, mailto) permitted
|
|
|
|
#### Styling Markdown Content
|
|
```css
|
|
.markdown-content h1,
|
|
.markdown-content h2,
|
|
.markdown-content h3 {
|
|
font-family: var(--theme-font-heading);
|
|
color: var(--theme-primary);
|
|
margin: 1.5em 0 0.5em 0;
|
|
}
|
|
|
|
.markdown-content strong {
|
|
color: var(--theme-accent);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.markdown-content code {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
padding: 0.2em 0.4em;
|
|
border-radius: 3px;
|
|
font-family: var(--theme-font-code);
|
|
}
|
|
|
|
.markdown-content blockquote {
|
|
border-left: 4px solid var(--theme-accent);
|
|
padding-left: 1rem;
|
|
font-style: italic;
|
|
}
|
|
```
|
|
|
|
#### Auto-Detection
|
|
Text slots automatically detect markdown patterns and apply markdown rendering:
|
|
- Content with `#`, `**`, `*`, `-`, `>`, etc. is processed as markdown
|
|
- Set `data-type="markdown"` to force markdown processing
|
|
- Set `data-type="text"` to disable auto-detection
|
|
|
|
## Best Practices
|
|
|
|
1. **Use clamp() for responsive typography**: `font-size: clamp(min, preferred, max)`
|
|
2. **Leverage CSS custom properties**: Makes themes easily customizable
|
|
3. **Follow existing naming conventions**: `.layout-[name]`, `.slot[data-slot="name"]`
|
|
4. **Test across aspect ratios**: Ensure layouts work in 16:9, 4:3, and 16:10
|
|
5. **Consider accessibility**: Use sufficient color contrast and readable fonts
|
|
6. **Use semantic HTML**: Proper heading hierarchy and meaningful class names
|
|
7. **Keep layouts flexible**: Use flexbox/grid for responsive behavior
|
|
8. **Use markdown for rich content**: Enable `data-type="markdown"` for formatted text slots
|
|
|
|
## Template Variables (Handlebars)
|
|
|
|
Use Handlebars syntax for dynamic content:
|
|
- `{{variableName}}` - Simple variable
|
|
- `{{#if condition}}...{{/if}}` - Conditional rendering
|
|
- `{{#image}}...{{/image}}` - Check if image exists
|
|
|
|
Common variables:
|
|
- `{{title}}` - Slide title
|
|
- `{{content}}` - Main content
|
|
- `{{image}}` - Image source
|
|
- `{{imageAlt}}` - Image alt text
|
|
- `{{footerText}}` - Footer text |